| 8127 | if (!openai_tool_stream_update(fd, s, r, id, &st->tool, raw, raw_len)) return false; |
| 8128 | if (!st->tool.active) st->mode = OPENAI_STREAM_SUPPRESS; |
| 8129 | } |
| 8130 | return true; |
| 8131 | } |
| 8132 | |
| 8133 | static bool openai_sse_finish_live(int fd, server *s, const request *r, const char *id, |
| 8134 | openai_stream *st, const char *raw, |
| 8135 | size_t raw_len, const tool_calls *calls, |
| 8136 | const char *finish, int prompt_tokens, |
| 8137 | int completion_tokens) { |
| 8138 | if (!openai_sse_stream_update(fd, s, r, id, st, raw, raw_len, true)) return false; |
| 8139 | |
| 8140 | buf b = {0}; |
| 8141 | long now = (long)time(NULL); |
| 8142 | if (calls && calls->len && !st->tool.emitted_any) { |
| 8143 | buf_printf(&b, "data: {\"id\":\"%s\",\"object\":\"chat.completion.chunk\",\"created\":%ld,\"model\":", id, now); |
| 8144 | json_escape(&b, r->model); |
| 8145 | buf_puts(&b, ",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":"); |
| 8146 | append_tool_call_deltas_json(&b, calls, id, &r->tool_orders); |
| 8147 | buf_puts(&b, "},\"finish_reason\":null}]}\n\n"); |
| 8148 | } |
| 8149 | buf_printf(&b, "data: {\"id\":\"%s\",\"object\":\"chat.completion.chunk\",\"created\":%ld,\"model\":", id, now); |
| 8150 | json_escape(&b, r->model); |
| 8151 | buf_puts(&b, ",\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":"); |
| 8152 | json_escape(&b, finish); |
| 8153 | buf_puts(&b, "}]}\n\n"); |
| 8154 | |
| 8155 | bool ok = send_all(fd, b.ptr, b.len) && |
| 8156 | sse_done(fd, r, id, prompt_tokens, completion_tokens); |
| 8157 | buf_free(&b); |
| 8158 | return ok; |
| 8159 | } |
| 8160 | |
| 8161 | static bool request_uses_openai_live_stream(const request *r) { |
| 8162 | return r->stream && r->api == API_OPENAI && r->kind == REQ_CHAT; |
| 8163 | } |
| 8164 | |
| 8165 | static bool request_uses_responses_live_stream(const request *r) { |
| 8166 | return r->stream && r->api == API_RESPONSES && r->kind == REQ_CHAT; |
| 8167 | } |
| 8168 | |
| 8169 | static bool request_uses_structured_stream(const request *r) { |
| 8170 | return r->stream && (r->api == API_ANTHROPIC || |
| 8171 | r->api == API_RESPONSES || |
| 8172 | request_uses_openai_live_stream(r)); |
| 8173 | } |
| 8174 | |
| 8175 | /* Codex' Responses API uses 24-hex suffixes for response/item ids. Prefix |
| 8176 | * controls the variant (resp_, rs_, msg_, fc_) so each event references a |
| 8177 | * stable identifier across output_item.added / .done. */ |
| 8178 | static void responses_random_id(char *dst, size_t dstlen, const char *prefix) { |
| 8179 | unsigned char bytes[12]; |
| 8180 | size_t pos = snprintf(dst, dstlen, "%s", prefix); |
| 8181 | if (pos >= dstlen) return; |
| 8182 | static uint64_t fallback_ctr; |
| 8183 | if (!random_bytes(bytes, sizeof(bytes))) { |
| 8184 | uint64_t a = ((uint64_t)time(NULL) << 32) ^ (uint64_t)getpid(); |
| 8185 | uint64_t b = ++fallback_ctr ^ (uint64_t)(uintptr_t)dst; |