codexmate 0.0.53 → 0.0.55
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 +1 -0
- package/README.vi.md +170 -0
- package/cli/builtin-proxy.js +222 -11
- package/cli/openai-bridge.js +238 -14
- package/cli.js +553 -5
- package/package.json +1 -1
- package/web-ui/app.js +38 -0
- package/web-ui/logic.claude.mjs +8 -0
- package/web-ui/modules/app.methods.claude-config.mjs +72 -5
- package/web-ui/modules/app.methods.index.mjs +4 -0
- package/web-ui/modules/app.methods.navigation.mjs +3 -0
- package/web-ui/modules/app.methods.provider-cache.mjs +223 -0
- package/web-ui/modules/app.methods.session-actions.mjs +12 -0
- package/web-ui/modules/app.methods.session-browser.mjs +3 -0
- package/web-ui/modules/app.methods.session-trash.mjs +3 -0
- package/web-ui/modules/app.methods.startup-claude.mjs +13 -3
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +161 -0
- package/web-ui/modules/i18n/locales/en.mjs +65 -1
- package/web-ui/modules/i18n/locales/ja.mjs +62 -1
- package/web-ui/modules/i18n/locales/vi.mjs +917 -0
- package/web-ui/modules/i18n/locales/zh-tw.mjs +62 -1
- package/web-ui/modules/i18n/locales/zh.mjs +62 -1
- package/web-ui/partials/index/layout-header.html +18 -5
- package/web-ui/partials/index/modals-basic.html +182 -0
- package/web-ui/partials/index/panel-sessions.html +2 -2
- package/web-ui/partials/index/panel-settings.html +20 -0
- package/web-ui/res/web-ui-render.precompiled.js +397 -20
- package/web-ui/styles/layout-shell.css +51 -1
- package/web-ui/styles/settings-panel.css +398 -0
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<a href="https://sakurabytecore.github.io/codexmate/">[Documentation]</a>
|
|
11
11
|
<a href="#quick-start">[Quick Start]</a>
|
|
12
12
|
<a href="README.zh.md">[简体中文]</a>
|
|
13
|
+
<a href="README.vi.md">[Tiếng Việt]</a>
|
|
13
14
|
</p>
|
|
14
15
|
|
|
15
16
|
[](https://www.npmjs.com/package/codexmate)
|
package/README.vi.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="site/.vitepress/public/images/logo.png" alt="Codex Mate logo" width="160" />
|
|
4
|
+
|
|
5
|
+
# Codex Mate
|
|
6
|
+
|
|
7
|
+
**Một dashboard duy nhất cho tất cả AI coding agent cục bộ của bạn. Chuyển đổi provider, quản lý phiên làm việc và điều phối tác vụ giữa Codex, Claude Code và OpenClaw. Không cloud, trung tâm điều khiển agent hoàn toàn cục bộ.**
|
|
8
|
+
|
|
9
|
+
<p>
|
|
10
|
+
<a href="https://sakurabytecore.github.io/codexmate/">[Tài liệu]</a>
|
|
11
|
+
<a href="#bắt-đầu-nhanh">[Bắt đầu nhanh]</a>
|
|
12
|
+
<a href="README.md">[English]</a>
|
|
13
|
+
<a href="README.zh.md">[简体中文]</a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
[](https://www.npmjs.com/package/codexmate)
|
|
17
|
+
[](https://github.com/SakuraByteCore/codexmate/actions/workflows/release.yml)
|
|
18
|
+
[](https://www.npmjs.com/package/codexmate)
|
|
19
|
+
[](#bắt-đầu-nhanh)
|
|
20
|
+
[](#bắt-đầu-nhanh)
|
|
21
|
+
[](https://nodejs.org/)
|
|
22
|
+
[](LICENSE)
|
|
23
|
+
[](https://github.com/SakuraByteCore/codexmate/stargazers)
|
|
24
|
+
[](https://github.com/SakuraByteCore/codexmate/issues)
|
|
25
|
+
|
|
26
|
+
<br />
|
|
27
|
+
|
|
28
|
+
<img src="site/.vitepress/public/images/readme-hero.png" alt="Giao diện Codex Mate" width="100%" />
|
|
29
|
+
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
> [!TIP]
|
|
35
|
+
> **Ưu tiên cục bộ**: Toàn bộ cấu hình và phiên làm việc được lưu trong thư mục home của bạn. Không có telemetry, không cần tài khoản cloud.
|
|
36
|
+
|
|
37
|
+
> [!IMPORTANT]
|
|
38
|
+
> Dự án đang ở giai đoạn đầu. Chúng tôi đang tìm kiếm các nhà phát triển để cùng xây dựng hệ sinh thái agent cục bộ!
|
|
39
|
+
|
|
40
|
+
## Codex Mate là gì?
|
|
41
|
+
|
|
42
|
+
Bạn có bao giờ cảm thấy rối khi phải quản lý nhiều AI agent cục bộ? Mỗi tool lại có định dạng cấu hình, nơi lưu phiên và thư mục skill riêng.
|
|
43
|
+
|
|
44
|
+
**Codex Mate** cung cấp một trung tâm điều khiển thống nhất để giải quyết sự hỗn loạn đó. Đây là CLI + Web UI ưu tiên cục bộ, được thiết kế để quản lý [Codex](https://github.com/openai/codex), [Claude Code](https://github.com/anthropic-ai/claude-code) và [OpenClaw](https://github.com/moeru-ai/openclaw) một cách liền mạch.
|
|
45
|
+
|
|
46
|
+
### Điểm nổi bật
|
|
47
|
+
|
|
48
|
+
Khác với các wrapper đơn giản, Codex Mate hoạt động như một **Agent Bridge cục bộ**:
|
|
49
|
+
- **Trình duyệt phiên thống nhất**: Tìm kiếm và xuất phiên làm việc từ tất cả tool trong một nơi duy nhất.
|
|
50
|
+
- **Bridge tương thích OpenAI**: Dùng Codex với bất kỳ UI nào hỗ trợ OpenAI bằng cách chuẩn hóa Responses API.
|
|
51
|
+
- **Chợ skill cục bộ**: Chia sẻ và nhập skill giữa các app agent khác nhau.
|
|
52
|
+
- **Điều phối tác vụ**: Lập kế hoạch và thực thi tác vụ phức tạp với theo dõi phụ thuộc.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Tiến độ hiện tại
|
|
57
|
+
|
|
58
|
+
| Tính năng | Trạng thái | Mô tả |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| **Quản lý Provider** | ✅ | Chuyển đổi provider/model cho Codex, Claude và OpenClaw |
|
|
61
|
+
| **Đồng bộ Agent trực tiếp** | ✅ | Giám sát cấu hình & trạng thái Codex/Claude theo thời gian thực |
|
|
62
|
+
| **Trình duyệt phiên** | ✅ | Liệt kê, lọc và xuất phiên (Codex/Claude/Gemini) |
|
|
63
|
+
| **Phân tích sử dụng** | ✅ | Trực quan hóa xu hướng tin nhắn và dự án nổi bật |
|
|
64
|
+
| **Chợ skill cục bộ** | ✅ | Import/export skill giữa các app agent |
|
|
65
|
+
| **Hàng đợi tác vụ** | ✅ | Thực thi tác vụ theo DAG và xem log |
|
|
66
|
+
| **Bridge OpenAI** | ✅ | Chuyển đổi Codex Responses API sang định dạng OpenAI chuẩn |
|
|
67
|
+
| **Mẫu Prompt** | ✅ | Plugin prompt tái sử dụng được với hỗ trợ biến |
|
|
68
|
+
| **Tích hợp MCP** | ✅ | Expose tool và resource cục bộ qua MCP stdio |
|
|
69
|
+
| **Tự động cập nhật** | ✅ | Cập nhật nhanh qua `codexmate update` |
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Bắt đầu nhanh
|
|
74
|
+
|
|
75
|
+
### Cài đặt qua Homebrew (macOS / Linux)
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
brew tap SakuraByteCore/codexmate
|
|
79
|
+
brew install codexmate
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Yêu cầu [Node.js](https://nodejs.org/) (`brew install node` nếu chưa có).
|
|
83
|
+
|
|
84
|
+
### Cài đặt qua npm
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm install -g codexmate
|
|
88
|
+
codexmate run
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Nếu cổng mặc định `3737` bận, Codex Mate tự động thử cổng kế tiếp (`3738`, `3739`, ...). Để chỉ định cổng cố định:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
CODEXMATE_PORT=8080 codexmate run
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Windows PowerShell:
|
|
98
|
+
|
|
99
|
+
```powershell
|
|
100
|
+
$env:CODEXMATE_PORT=8080; codexmate run
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Cài đặt qua curl (Standalone)
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
curl -fsSL https://raw.githubusercontent.com/SakuraByteCore/codexmate/main/scripts/install.sh | bash
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Agent được hỗ trợ
|
|
110
|
+
|
|
111
|
+
- **Codex**: `npm install -g @openai/codex`
|
|
112
|
+
- **Claude Code**: `npm install -g @anthropic-ai/claude-code`
|
|
113
|
+
- **Gemini CLI**: `npm install -g @google/gemini-cli`
|
|
114
|
+
- **CodeBuddy**: `npm install -g @tencent-ai/codebuddy-code`
|
|
115
|
+
- **OpenCode**: xem [tài liệu chính thức OpenCode](https://opencode.ai/)
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Kiến trúc
|
|
120
|
+
|
|
121
|
+
```mermaid
|
|
122
|
+
%%{ init: { 'flowchart': { 'curve': 'catmullRom' } } }%%
|
|
123
|
+
flowchart TD
|
|
124
|
+
User([Người dùng])
|
|
125
|
+
CLI[CLI]
|
|
126
|
+
WebUI[Web UI]
|
|
127
|
+
MCP[MCP Server]
|
|
128
|
+
|
|
129
|
+
subgraph Mate [Codex Mate Core]
|
|
130
|
+
API[HTTP API]
|
|
131
|
+
Config[Config Engine]
|
|
132
|
+
Session[Quản lý phiên]
|
|
133
|
+
Skills[Chợ Skill]
|
|
134
|
+
Tasks[Thực thi tác vụ]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
subgraph Local [Hệ thống tệp cục bộ]
|
|
138
|
+
CodexDir[~/.codex]
|
|
139
|
+
ClaudeDir[~/.claude]
|
|
140
|
+
ClawDir[~/.openclaw]
|
|
141
|
+
State[Phiên/Sử dụng/Thùng rác]
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
User --> CLI & WebUI & MCP
|
|
145
|
+
CLI & WebUI & MCP --> API
|
|
146
|
+
|
|
147
|
+
API --> Config & Session & Skills & Tasks
|
|
148
|
+
|
|
149
|
+
Config --> CodexDir & ClaudeDir & ClawDir
|
|
150
|
+
Session --> State
|
|
151
|
+
Skills --> Local
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Lời cảm ơn
|
|
157
|
+
|
|
158
|
+
Cảm ơn tất cả những người đóng góp cho Codex Mate ❤️
|
|
159
|
+
|
|
160
|
+
<a href="https://github.com/SakuraByteCore/codexmate/graphs/contributors">
|
|
161
|
+
<img src="https://contrib.rocks/image?repo=SakuraByteCore/codexmate" alt="Contributors" />
|
|
162
|
+
</a>
|
|
163
|
+
|
|
164
|
+
## Lịch sử Star
|
|
165
|
+
|
|
166
|
+
[](https://star-history.com/#SakuraByteCore/codexmate&Date)
|
|
167
|
+
|
|
168
|
+
## Giấy phép
|
|
169
|
+
|
|
170
|
+
Apache-2.0
|
package/cli/builtin-proxy.js
CHANGED
|
@@ -940,7 +940,6 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
940
940
|
'n',
|
|
941
941
|
'modalities',
|
|
942
942
|
'audio',
|
|
943
|
-
'reasoning',
|
|
944
943
|
'reasoning_effort',
|
|
945
944
|
'service_tier'
|
|
946
945
|
];
|
|
@@ -964,10 +963,15 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
964
963
|
if (isRecord(source.text) && asTrimmedString(source.text.verbosity)) {
|
|
965
964
|
chatBody.verbosity = asTrimmedString(source.text.verbosity);
|
|
966
965
|
}
|
|
966
|
+
if (isRecord(source.reasoning) && asTrimmedString(source.reasoning.effort)) {
|
|
967
|
+
chatBody.reasoning_effort = asTrimmedString(source.reasoning.effort);
|
|
968
|
+
}
|
|
967
969
|
|
|
968
970
|
pruneInvalidChatToolChoice(chatBody);
|
|
969
971
|
|
|
970
|
-
if (Object.prototype.hasOwnProperty.call(source, '
|
|
972
|
+
if (Object.prototype.hasOwnProperty.call(source, 'max_completion_tokens')) {
|
|
973
|
+
chatBody.max_tokens = Math.max(128, Number(source.max_completion_tokens) || 0);
|
|
974
|
+
} else if (Object.prototype.hasOwnProperty.call(source, 'max_tokens')) {
|
|
971
975
|
chatBody.max_tokens = source.max_tokens;
|
|
972
976
|
} else if (source.max_output_tokens != null) {
|
|
973
977
|
chatBody.max_tokens = source.max_output_tokens;
|
|
@@ -976,6 +980,18 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
976
980
|
return chatBody;
|
|
977
981
|
}
|
|
978
982
|
|
|
983
|
+
function normalizeResponsesPayloadForUpstream(payload, stream) {
|
|
984
|
+
const source = isRecord(payload) ? payload : {};
|
|
985
|
+
const normalized = { ...source, stream };
|
|
986
|
+
if (isRecord(source.reasoning)) {
|
|
987
|
+
const include = Array.isArray(source.include) ? source.include.filter((item) => typeof item === 'string') : [];
|
|
988
|
+
if (!include.includes('reasoning.encrypted_content')) {
|
|
989
|
+
normalized.include = [...include, 'reasoning.encrypted_content'];
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
return normalized;
|
|
993
|
+
}
|
|
994
|
+
|
|
979
995
|
function ensureResponseMetadata(payload) {
|
|
980
996
|
const base = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload : {};
|
|
981
997
|
const id = typeof base.id === 'string' && base.id.trim()
|
|
@@ -1001,6 +1017,112 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
1001
1017
|
res.write(`data: ${JSON.stringify(dataObj)}\n\n`);
|
|
1002
1018
|
}
|
|
1003
1019
|
|
|
1020
|
+
function buildResponsesSseItem(item, fallbackId) {
|
|
1021
|
+
const source = isRecord(item) ? item : {};
|
|
1022
|
+
const itemId = asTrimmedString(source.id || source.call_id) || fallbackId || `item_${crypto.randomBytes(8).toString('hex')}`;
|
|
1023
|
+
if (source.type === 'message') {
|
|
1024
|
+
return {
|
|
1025
|
+
...source,
|
|
1026
|
+
id: itemId,
|
|
1027
|
+
role: source.role || 'assistant',
|
|
1028
|
+
content: Array.isArray(source.content)
|
|
1029
|
+
? source.content.map((part) => {
|
|
1030
|
+
if (!isRecord(part)) return part;
|
|
1031
|
+
if (part.type !== 'output_text') return cloneJsonValue(part);
|
|
1032
|
+
return {
|
|
1033
|
+
...part,
|
|
1034
|
+
text: typeof part.text === 'string' ? part.text : '',
|
|
1035
|
+
annotations: Array.isArray(part.annotations) ? part.annotations : [],
|
|
1036
|
+
logprobs: Array.isArray(part.logprobs) ? part.logprobs : []
|
|
1037
|
+
};
|
|
1038
|
+
})
|
|
1039
|
+
: []
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
if (source.type === 'reasoning') {
|
|
1043
|
+
return {
|
|
1044
|
+
...source,
|
|
1045
|
+
id: itemId,
|
|
1046
|
+
summary: Array.isArray(source.summary) ? source.summary : []
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
if (source.type === 'function_call') {
|
|
1050
|
+
return {
|
|
1051
|
+
...source,
|
|
1052
|
+
id: itemId,
|
|
1053
|
+
call_id: asTrimmedString(source.call_id) || itemId,
|
|
1054
|
+
name: asTrimmedString(source.name),
|
|
1055
|
+
arguments: typeof source.arguments === 'string' ? source.arguments : ''
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
if (source.type === 'custom_tool_call') {
|
|
1059
|
+
return {
|
|
1060
|
+
...source,
|
|
1061
|
+
id: itemId,
|
|
1062
|
+
call_id: asTrimmedString(source.call_id) || itemId,
|
|
1063
|
+
name: asTrimmedString(source.name),
|
|
1064
|
+
input: typeof source.input === 'string' ? source.input : ''
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
return { ...source, id: itemId };
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
function emitResponsesTextPartAdded(res, itemId, outputIndex, contentIndex, nextSeq) {
|
|
1071
|
+
writeSse(res, 'response.content_part.added', {
|
|
1072
|
+
type: 'response.content_part.added',
|
|
1073
|
+
item_id: itemId,
|
|
1074
|
+
output_index: outputIndex,
|
|
1075
|
+
content_index: contentIndex,
|
|
1076
|
+
part: { type: 'output_text', text: '', annotations: [], logprobs: [] },
|
|
1077
|
+
sequence_number: nextSeq()
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
function emitResponsesTextPartDone(res, itemId, outputIndex, contentIndex, text, nextSeq) {
|
|
1082
|
+
writeSse(res, 'response.content_part.done', {
|
|
1083
|
+
type: 'response.content_part.done',
|
|
1084
|
+
item_id: itemId,
|
|
1085
|
+
output_index: outputIndex,
|
|
1086
|
+
content_index: contentIndex,
|
|
1087
|
+
part: { type: 'output_text', text: typeof text === 'string' ? text : '', annotations: [], logprobs: [] },
|
|
1088
|
+
sequence_number: nextSeq()
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
function emitResponsesToolArgumentEvents(res, item, outputIndex, nextSeq) {
|
|
1093
|
+
const eventType = item.type === 'custom_tool_call'
|
|
1094
|
+
? 'response.custom_tool_call_input.delta'
|
|
1095
|
+
: 'response.function_call_arguments.delta';
|
|
1096
|
+
const doneType = item.type === 'custom_tool_call'
|
|
1097
|
+
? ''
|
|
1098
|
+
: 'response.function_call_arguments.done';
|
|
1099
|
+
const value = item.type === 'custom_tool_call'
|
|
1100
|
+
? (typeof item.input === 'string' ? item.input : '')
|
|
1101
|
+
: (typeof item.arguments === 'string' ? item.arguments : '');
|
|
1102
|
+
if (value) {
|
|
1103
|
+
writeSse(res, eventType, {
|
|
1104
|
+
type: eventType,
|
|
1105
|
+
item_id: item.id,
|
|
1106
|
+
output_index: outputIndex,
|
|
1107
|
+
call_id: item.call_id,
|
|
1108
|
+
name: item.name,
|
|
1109
|
+
delta: value,
|
|
1110
|
+
sequence_number: nextSeq()
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
if (doneType) {
|
|
1114
|
+
writeSse(res, doneType, {
|
|
1115
|
+
type: doneType,
|
|
1116
|
+
item_id: item.id,
|
|
1117
|
+
output_index: outputIndex,
|
|
1118
|
+
call_id: item.call_id,
|
|
1119
|
+
name: item.name,
|
|
1120
|
+
arguments: value,
|
|
1121
|
+
sequence_number: nextSeq()
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1004
1126
|
function sendResponsesSse(res, responsePayload) {
|
|
1005
1127
|
const response = ensureResponseMetadata(responsePayload);
|
|
1006
1128
|
const responseId = response.id;
|
|
@@ -1027,12 +1149,14 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
1027
1149
|
const itemType = typeof item.type === 'string' ? item.type : '';
|
|
1028
1150
|
const itemId = typeof item.id === 'string' && item.id.trim()
|
|
1029
1151
|
? item.id.trim()
|
|
1030
|
-
: `item_${crypto.randomBytes(8).toString('hex')}
|
|
1152
|
+
: (typeof item.call_id === 'string' && item.call_id.trim() ? item.call_id.trim() : `item_${crypto.randomBytes(8).toString('hex')}`);
|
|
1153
|
+
const wireItem = buildResponsesSseItem(item, itemId);
|
|
1031
1154
|
|
|
1032
1155
|
writeSse(res, 'response.output_item.added', {
|
|
1033
1156
|
type: 'response.output_item.added',
|
|
1034
1157
|
output_index: outputIndex,
|
|
1035
|
-
item:
|
|
1158
|
+
item: wireItem,
|
|
1159
|
+
sequence_number: nextSeq()
|
|
1036
1160
|
});
|
|
1037
1161
|
|
|
1038
1162
|
if (itemType === 'message') {
|
|
@@ -1042,6 +1166,7 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
1042
1166
|
if (!block || typeof block !== 'object') continue;
|
|
1043
1167
|
if (block.type !== 'output_text') continue;
|
|
1044
1168
|
const text = typeof block.text === 'string' ? block.text : '';
|
|
1169
|
+
emitResponsesTextPartAdded(res, itemId, outputIndex, contentIndex, nextSeq);
|
|
1045
1170
|
if (text) {
|
|
1046
1171
|
writeSse(res, 'response.output_text.delta', {
|
|
1047
1172
|
type: 'response.output_text.delta',
|
|
@@ -1060,13 +1185,40 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
1060
1185
|
text,
|
|
1061
1186
|
sequence_number: nextSeq()
|
|
1062
1187
|
});
|
|
1188
|
+
emitResponsesTextPartDone(res, itemId, outputIndex, contentIndex, text, nextSeq);
|
|
1189
|
+
}
|
|
1190
|
+
} else if (itemType === 'function_call' || itemType === 'custom_tool_call') {
|
|
1191
|
+
emitResponsesToolArgumentEvents(res, wireItem, outputIndex, nextSeq);
|
|
1192
|
+
} else if (itemType === 'reasoning') {
|
|
1193
|
+
const summary = Array.isArray(item.summary) ? item.summary : [];
|
|
1194
|
+
for (let summaryIndex = 0; summaryIndex < summary.length; summaryIndex += 1) {
|
|
1195
|
+
const block = summary[summaryIndex];
|
|
1196
|
+
const text = block && typeof block.text === 'string' ? block.text : '';
|
|
1197
|
+
if (text) {
|
|
1198
|
+
writeSse(res, 'response.reasoning_summary_text.delta', {
|
|
1199
|
+
type: 'response.reasoning_summary_text.delta',
|
|
1200
|
+
item_id: itemId,
|
|
1201
|
+
output_index: outputIndex,
|
|
1202
|
+
summary_index: summaryIndex,
|
|
1203
|
+
delta: text,
|
|
1204
|
+
sequence_number: nextSeq()
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
writeSse(res, 'response.reasoning_summary_text.done', {
|
|
1208
|
+
type: 'response.reasoning_summary_text.done',
|
|
1209
|
+
item_id: itemId,
|
|
1210
|
+
output_index: outputIndex,
|
|
1211
|
+
summary_index: summaryIndex,
|
|
1212
|
+
text,
|
|
1213
|
+
sequence_number: nextSeq()
|
|
1214
|
+
});
|
|
1063
1215
|
}
|
|
1064
1216
|
}
|
|
1065
1217
|
|
|
1066
1218
|
writeSse(res, 'response.output_item.done', {
|
|
1067
1219
|
type: 'response.output_item.done',
|
|
1068
1220
|
output_index: outputIndex,
|
|
1069
|
-
item:
|
|
1221
|
+
item: wireItem,
|
|
1070
1222
|
sequence_number: nextSeq()
|
|
1071
1223
|
});
|
|
1072
1224
|
}
|
|
@@ -1105,6 +1257,40 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
1105
1257
|
const delta = choice && choice.delta && typeof choice.delta === 'object' ? choice.delta : null;
|
|
1106
1258
|
if (!delta) continue;
|
|
1107
1259
|
|
|
1260
|
+
const reasoningSegment = typeof delta.reasoning_content === 'string'
|
|
1261
|
+
? delta.reasoning_content
|
|
1262
|
+
: (typeof delta.reasoning === 'string'
|
|
1263
|
+
? delta.reasoning
|
|
1264
|
+
: (typeof delta.reasoning_text === 'string' ? delta.reasoning_text : ''));
|
|
1265
|
+
if (reasoningSegment) {
|
|
1266
|
+
if (!state.reasoningItem) {
|
|
1267
|
+
state.reasoningItem = {
|
|
1268
|
+
id: `rs_${crypto.randomBytes(8).toString('hex')}`,
|
|
1269
|
+
type: 'reasoning',
|
|
1270
|
+
summary: [{ type: 'summary_text', text: '' }]
|
|
1271
|
+
};
|
|
1272
|
+
state.output.push(state.reasoningItem);
|
|
1273
|
+
state.outputStarted = true;
|
|
1274
|
+
beginChatStreamResponsesSse(state);
|
|
1275
|
+
writeSse(state.res, 'response.output_item.added', {
|
|
1276
|
+
type: 'response.output_item.added',
|
|
1277
|
+
output_index: state.output.length - 1,
|
|
1278
|
+
item: buildResponsesSseItem(state.reasoningItem, state.reasoningItem.id),
|
|
1279
|
+
sequence_number: state.nextSeq()
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
state.reasoningText += reasoningSegment;
|
|
1283
|
+
state.reasoningItem.summary[0].text = state.reasoningText;
|
|
1284
|
+
writeSse(state.res, 'response.reasoning_summary_text.delta', {
|
|
1285
|
+
type: 'response.reasoning_summary_text.delta',
|
|
1286
|
+
item_id: state.reasoningItem.id,
|
|
1287
|
+
output_index: state.output.indexOf(state.reasoningItem),
|
|
1288
|
+
summary_index: 0,
|
|
1289
|
+
delta: reasoningSegment,
|
|
1290
|
+
sequence_number: state.nextSeq()
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1108
1294
|
if (typeof delta.content === 'string' && delta.content) {
|
|
1109
1295
|
if (!state.messageItem) {
|
|
1110
1296
|
state.messageItem = {
|
|
@@ -1119,8 +1305,10 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
1119
1305
|
writeSse(state.res, 'response.output_item.added', {
|
|
1120
1306
|
type: 'response.output_item.added',
|
|
1121
1307
|
output_index: state.output.length - 1,
|
|
1122
|
-
item: state.messageItem
|
|
1308
|
+
item: buildResponsesSseItem(state.messageItem, state.messageItem.id),
|
|
1309
|
+
sequence_number: state.nextSeq()
|
|
1123
1310
|
});
|
|
1311
|
+
emitResponsesTextPartAdded(state.res, state.messageItem.id, state.output.length - 1, 0, state.nextSeq);
|
|
1124
1312
|
}
|
|
1125
1313
|
state.messageText += delta.content;
|
|
1126
1314
|
state.messageItem.content[0].text = state.messageText;
|
|
@@ -1172,6 +1360,24 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
1172
1360
|
state.finished = true;
|
|
1173
1361
|
stopChatStreamHeartbeat(state);
|
|
1174
1362
|
|
|
1363
|
+
if (state.reasoningItem) {
|
|
1364
|
+
const outputIndex = state.output.indexOf(state.reasoningItem);
|
|
1365
|
+
writeSse(state.res, 'response.reasoning_summary_text.done', {
|
|
1366
|
+
type: 'response.reasoning_summary_text.done',
|
|
1367
|
+
item_id: state.reasoningItem.id,
|
|
1368
|
+
output_index: outputIndex,
|
|
1369
|
+
summary_index: 0,
|
|
1370
|
+
text: state.reasoningText,
|
|
1371
|
+
sequence_number: state.nextSeq()
|
|
1372
|
+
});
|
|
1373
|
+
writeSse(state.res, 'response.output_item.done', {
|
|
1374
|
+
type: 'response.output_item.done',
|
|
1375
|
+
output_index: outputIndex,
|
|
1376
|
+
item: buildResponsesSseItem(state.reasoningItem, state.reasoningItem.id),
|
|
1377
|
+
sequence_number: state.nextSeq()
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1175
1381
|
if (state.messageItem) {
|
|
1176
1382
|
const outputIndex = state.output.indexOf(state.messageItem);
|
|
1177
1383
|
writeSse(state.res, 'response.output_text.done', {
|
|
@@ -1182,10 +1388,11 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
1182
1388
|
text: state.messageText,
|
|
1183
1389
|
sequence_number: state.nextSeq()
|
|
1184
1390
|
});
|
|
1391
|
+
emitResponsesTextPartDone(state.res, state.messageItem.id, outputIndex, 0, state.messageText, state.nextSeq);
|
|
1185
1392
|
writeSse(state.res, 'response.output_item.done', {
|
|
1186
1393
|
type: 'response.output_item.done',
|
|
1187
1394
|
output_index: outputIndex,
|
|
1188
|
-
item: state.messageItem,
|
|
1395
|
+
item: buildResponsesSseItem(state.messageItem, state.messageItem.id),
|
|
1189
1396
|
sequence_number: state.nextSeq()
|
|
1190
1397
|
});
|
|
1191
1398
|
}
|
|
@@ -1200,12 +1407,14 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
1200
1407
|
writeSse(state.res, 'response.output_item.added', {
|
|
1201
1408
|
type: 'response.output_item.added',
|
|
1202
1409
|
output_index: outputIndex,
|
|
1203
|
-
item
|
|
1410
|
+
item: buildResponsesSseItem(item, item.call_id),
|
|
1411
|
+
sequence_number: state.nextSeq()
|
|
1204
1412
|
});
|
|
1413
|
+
emitResponsesToolArgumentEvents(state.res, buildResponsesSseItem(item, item.call_id), outputIndex, state.nextSeq);
|
|
1205
1414
|
writeSse(state.res, 'response.output_item.done', {
|
|
1206
1415
|
type: 'response.output_item.done',
|
|
1207
1416
|
output_index: outputIndex,
|
|
1208
|
-
item,
|
|
1417
|
+
item: buildResponsesSseItem(item, item.call_id),
|
|
1209
1418
|
sequence_number: state.nextSeq()
|
|
1210
1419
|
});
|
|
1211
1420
|
}
|
|
@@ -1282,6 +1491,8 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
1282
1491
|
output: [],
|
|
1283
1492
|
messageItem: null,
|
|
1284
1493
|
messageText: '',
|
|
1494
|
+
reasoningItem: null,
|
|
1495
|
+
reasoningText: '',
|
|
1285
1496
|
toolCalls: [],
|
|
1286
1497
|
toolTypesByName: options.toolTypesByName || {},
|
|
1287
1498
|
finished: false,
|
|
@@ -1983,7 +2194,7 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
1983
2194
|
method: 'POST',
|
|
1984
2195
|
headers: commonHeaders,
|
|
1985
2196
|
timeoutMs,
|
|
1986
|
-
body:
|
|
2197
|
+
body: normalizeResponsesPayloadForUpstream(payload, true),
|
|
1987
2198
|
res,
|
|
1988
2199
|
model
|
|
1989
2200
|
});
|
|
@@ -2031,7 +2242,7 @@ function createBuiltinProxyRuntimeController(deps = {}) {
|
|
|
2031
2242
|
method: 'POST',
|
|
2032
2243
|
headers: commonHeaders,
|
|
2033
2244
|
timeoutMs,
|
|
2034
|
-
body:
|
|
2245
|
+
body: normalizeResponsesPayloadForUpstream(payload, false)
|
|
2035
2246
|
});
|
|
2036
2247
|
|
|
2037
2248
|
// 优先走上游 /responses(如果支持)。若上游报错且不是“端点不支持”,则直接透传错误。
|