|
7 | 7 | "fmt" |
8 | 8 | "io" |
9 | 9 | "net/http" |
| 10 | + "sort" |
10 | 11 | "strings" |
11 | 12 | "time" |
12 | 13 |
|
@@ -167,22 +168,63 @@ func (e *CodexExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, re |
167 | 168 | helps.AppendAPIResponseChunk(ctx, e.cfg, data) |
168 | 169 |
|
169 | 170 | lines := bytes.Split(data, []byte("\n")) |
| 171 | + outputItemsByIndex := make(map[int64][]byte) |
| 172 | + var outputItemsFallback [][]byte |
170 | 173 | for _, line := range lines { |
171 | 174 | if !bytes.HasPrefix(line, dataTag) { |
172 | 175 | continue |
173 | 176 | } |
174 | 177 |
|
175 | | - line = bytes.TrimSpace(line[5:]) |
176 | | - if gjson.GetBytes(line, "type").String() != "response.completed" { |
| 178 | + eventData := bytes.TrimSpace(line[5:]) |
| 179 | + eventType := gjson.GetBytes(eventData, "type").String() |
| 180 | + |
| 181 | + if eventType == "response.output_item.done" { |
| 182 | + itemResult := gjson.GetBytes(eventData, "item") |
| 183 | + if !itemResult.Exists() || itemResult.Type != gjson.JSON { |
| 184 | + continue |
| 185 | + } |
| 186 | + outputIndexResult := gjson.GetBytes(eventData, "output_index") |
| 187 | + if outputIndexResult.Exists() { |
| 188 | + outputItemsByIndex[outputIndexResult.Int()] = []byte(itemResult.Raw) |
| 189 | + } else { |
| 190 | + outputItemsFallback = append(outputItemsFallback, []byte(itemResult.Raw)) |
| 191 | + } |
| 192 | + continue |
| 193 | + } |
| 194 | + |
| 195 | + if eventType != "response.completed" { |
177 | 196 | continue |
178 | 197 | } |
179 | 198 |
|
180 | | - if detail, ok := helps.ParseCodexUsage(line); ok { |
| 199 | + if detail, ok := helps.ParseCodexUsage(eventData); ok { |
181 | 200 | reporter.Publish(ctx, detail) |
182 | 201 | } |
183 | 202 |
|
| 203 | + completedData := eventData |
| 204 | + outputResult := gjson.GetBytes(completedData, "response.output") |
| 205 | + shouldPatchOutput := (!outputResult.Exists() || !outputResult.IsArray() || len(outputResult.Array()) == 0) && (len(outputItemsByIndex) > 0 || len(outputItemsFallback) > 0) |
| 206 | + if shouldPatchOutput { |
| 207 | + completedDataPatched := completedData |
| 208 | + completedDataPatched, _ = sjson.SetRawBytes(completedDataPatched, "response.output", []byte(`[]`)) |
| 209 | + |
| 210 | + indexes := make([]int64, 0, len(outputItemsByIndex)) |
| 211 | + for idx := range outputItemsByIndex { |
| 212 | + indexes = append(indexes, idx) |
| 213 | + } |
| 214 | + sort.Slice(indexes, func(i, j int) bool { |
| 215 | + return indexes[i] < indexes[j] |
| 216 | + }) |
| 217 | + for _, idx := range indexes { |
| 218 | + completedDataPatched, _ = sjson.SetRawBytes(completedDataPatched, "response.output.-1", outputItemsByIndex[idx]) |
| 219 | + } |
| 220 | + for _, item := range outputItemsFallback { |
| 221 | + completedDataPatched, _ = sjson.SetRawBytes(completedDataPatched, "response.output.-1", item) |
| 222 | + } |
| 223 | + completedData = completedDataPatched |
| 224 | + } |
| 225 | + |
184 | 226 | var param any |
185 | | - out := sdktranslator.TranslateNonStream(ctx, to, from, req.Model, originalPayload, body, line, ¶m) |
| 227 | + out := sdktranslator.TranslateNonStream(ctx, to, from, req.Model, originalPayload, body, completedData, ¶m) |
186 | 228 | resp = cliproxyexecutor.Response{Payload: out, Headers: httpResp.Header.Clone()} |
187 | 229 | return resp, nil |
188 | 230 | } |
|
0 commit comments