| 237 | choice["finish_reason"] = c.value("finish_reason", "stop"); |
| 238 | json msg = {{"role", "assistant"}, {"content", c.value("text", "")}}; |
| 239 | choice["message"] = msg; |
| 240 | choices.push_back(choice); |
| 241 | } |
| 242 | } |
| 243 | chat_resp["choices"] = choices; |
| 244 | return chat_resp; |
| 245 | } |
| 246 | |
| 247 | static bool curl_forward(SocketHandle client_fd, const std::string & url, |
| 248 | const std::string & api_key, const json & body, |
| 249 | bool streaming, bool rewrite_to_chat, |
| 250 | const std::string & response_id, |
| 251 | const std::string & model) { |
| 252 | CURL * curl = curl_easy_init(); |
| 253 | if (!curl) return false; |
| 254 | |
| 255 | std::string body_str = body.dump(); |
| 256 | |
| 257 | struct curl_slist * headers = nullptr; |
| 258 | headers = curl_slist_append(headers, "Content-Type: application/json"); |
| 259 | if (!api_key.empty()) { |
| 260 | std::string auth = "Authorization: Bearer " + api_key; |
| 261 | headers = curl_slist_append(headers, auth.c_str()); |
| 262 | } |
| 263 | |
| 264 | CurlWriteCtx ctx; |
| 265 | ctx.client_fd = client_fd; |
| 266 | ctx.streaming = streaming; |
| 267 | ctx.first_chunk = true; |
| 268 | ctx.chat_rewrite = rewrite_to_chat; |
| 269 | ctx.response_id = response_id; |
| 270 | ctx.model = model; |
| 271 | |
| 272 | curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); |
| 273 | curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body_str.c_str()); |
| 274 | curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)body_str.size()); |
| 275 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); |
| 276 | curl_easy_setopt(curl, CURLOPT_TIMEOUT, 600L); |
| 277 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ctx); |
| 278 | |
| 279 | if (rewrite_to_chat) { |
| 280 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_rewrite); |
| 281 | } else { |
| 282 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_passthrough); |
| 283 | } |
| 284 | |
| 285 | if (streaming) { |
| 286 | std::string sse_header = |
| 287 | "HTTP/1.1 200 OK\r\n" |
| 288 | "Content-Type: text/event-stream\r\n" |
| 289 | "Cache-Control: no-cache\r\n" |
| 290 | "Connection: keep-alive\r\n" |
| 291 | "\r\n"; |
| 292 | ::send(client_fd, sse_header.data(), sse_header.size(), MSG_NOSIGNAL); |
| 293 | } |
| 294 | |
| 295 | CURLcode res = curl_easy_perform(curl); |
| 296 |
no test coverage detected