Spaces:
Paused
Paused
| from __future__ import annotations | |
| import time | |
| import uuid | |
| from typing import Any, Iterable, Iterator | |
| from fastapi import HTTPException | |
| from services.protocol.conversation import ( | |
| ConversationRequest, | |
| ImageOutput, | |
| collect_image_outputs, | |
| collect_text, | |
| count_message_tokens, | |
| count_text_tokens, | |
| encode_images, | |
| normalize_messages, | |
| stream_image_outputs_with_pool, | |
| stream_text_deltas, | |
| text_backend, | |
| ) | |
| from utils.helper import build_chat_image_markdown_content, extract_chat_image, extract_chat_prompt, is_image_chat_request, parse_image_count | |
| def completion_chunk(model: str, delta: dict[str, Any], finish_reason: str | None = None, completion_id: str = "", created: int | None = None) -> dict[str, Any]: | |
| return { | |
| "id": completion_id or f"chatcmpl-{uuid.uuid4().hex}", | |
| "object": "chat.completion.chunk", | |
| "created": created or int(time.time()), | |
| "model": model, | |
| "choices": [{"index": 0, "delta": delta, "finish_reason": finish_reason}], | |
| } | |
| def completion_response( | |
| model: str, | |
| content: str, | |
| created: int | None = None, | |
| messages: list[dict[str, Any]] | None = None, | |
| ) -> dict[str, Any]: | |
| prompt_tokens = count_message_tokens(messages, model) if messages else 0 | |
| completion_tokens = count_text_tokens(content, model) if messages else 0 | |
| return { | |
| "id": f"chatcmpl-{uuid.uuid4().hex}", | |
| "object": "chat.completion", | |
| "created": created or int(time.time()), | |
| "model": model, | |
| "choices": [{ | |
| "index": 0, | |
| "message": {"role": "assistant", "content": content}, | |
| "finish_reason": "stop", | |
| }], | |
| "usage": { | |
| "prompt_tokens": prompt_tokens, | |
| "completion_tokens": completion_tokens, | |
| "total_tokens": prompt_tokens + completion_tokens, | |
| }, | |
| } | |
| def stream_text_chat_completion(backend, messages: list[dict[str, Any]], model: str) -> Iterator[dict[str, Any]]: | |
| completion_id = f"chatcmpl-{uuid.uuid4().hex}" | |
| created = int(time.time()) | |
| sent_role = False | |
| request = ConversationRequest(model=model, messages=messages) | |
| for delta_text in stream_text_deltas(backend, request): | |
| if not sent_role: | |
| sent_role = True | |
| yield completion_chunk(model, {"role": "assistant", "content": delta_text}, None, completion_id, created) | |
| else: | |
| yield completion_chunk(model, {"content": delta_text}, None, completion_id, created) | |
| if not sent_role: | |
| yield completion_chunk(model, {"role": "assistant", "content": ""}, None, completion_id, created) | |
| yield completion_chunk(model, {}, "stop", completion_id, created) | |
| def collect_chat_content(chunks: Iterable[dict[str, Any]]) -> str: | |
| parts: list[str] = [] | |
| for chunk in chunks: | |
| choices = chunk.get("choices") | |
| first = choices[0] if isinstance(choices, list) and choices and isinstance(choices[0], dict) else {} | |
| delta = first.get("delta") if isinstance(first.get("delta"), dict) else {} | |
| content = str(delta.get("content") or "") | |
| if content: | |
| parts.append(content) | |
| return "".join(parts) | |
| def chat_messages_from_body(body: dict[str, Any]) -> list[dict[str, Any]]: | |
| messages = body.get("messages") | |
| if isinstance(messages, list) and messages: | |
| return [message for message in messages if isinstance(message, dict)] | |
| prompt = str(body.get("prompt") or "").strip() | |
| if prompt: | |
| return [{"role": "user", "content": prompt}] | |
| raise HTTPException(status_code=400, detail={"error": "messages or prompt is required"}) | |
| def chat_image_args(body: dict[str, Any]) -> tuple[str, str, int, list[tuple[bytes, str, str]]]: | |
| model = str(body.get("model") or "gpt-image-2").strip() or "gpt-image-2" | |
| prompt = extract_chat_prompt(body) | |
| if not prompt: | |
| raise HTTPException(status_code=400, detail={"error": "prompt is required"}) | |
| images = [ | |
| (data, f"image_{idx}.png", mime) | |
| for idx, (data, mime) in enumerate(extract_chat_image(body), start=1) | |
| ] | |
| return model, prompt, parse_image_count(body.get("n")), images | |
| def text_chat_parts(body: dict[str, Any]) -> tuple[str, list[dict[str, Any]]]: | |
| model = str(body.get("model") or "auto").strip() or "auto" | |
| messages = normalize_messages(chat_messages_from_body(body)) | |
| return model, messages | |
| def image_result_content(result: dict[str, Any]) -> str: | |
| data = result.get("data") | |
| if isinstance(data, list) and data: | |
| return build_chat_image_markdown_content(result) | |
| return str(result.get("message") or "Image generation completed.") | |
| def image_chat_response(body: dict[str, Any]) -> dict[str, Any]: | |
| model, prompt, n, images = chat_image_args(body) | |
| result = collect_image_outputs(stream_image_outputs_with_pool(ConversationRequest( | |
| prompt=prompt, | |
| model=model, | |
| n=n, | |
| response_format="b64_json", | |
| images=encode_images(images) or None, | |
| ))) | |
| return completion_response(model, image_result_content(result), int(result.get("created") or 0) or None) | |
| def image_chat_events(body: dict[str, Any]) -> Iterator[dict[str, Any]]: | |
| model, prompt, n, images = chat_image_args(body) | |
| image_outputs = stream_image_outputs_with_pool(ConversationRequest( | |
| prompt=prompt, | |
| model=model, | |
| n=n, | |
| response_format="b64_json", | |
| images=encode_images(images) or None, | |
| )) | |
| yield from stream_image_chat_completion(image_outputs, model) | |
| def stream_image_chat_completion(image_outputs: Iterable[ImageOutput], model: str) -> Iterator[dict[str, Any]]: | |
| completion_id = f"chatcmpl-{uuid.uuid4().hex}" | |
| created = int(time.time()) | |
| sent_role = False | |
| sent_text = "" | |
| for output in image_outputs: | |
| content = "" | |
| if output.kind == "progress": | |
| content = output.text | |
| sent_text += content | |
| elif output.kind == "result": | |
| content = build_chat_image_markdown_content({"data": output.data}) | |
| elif output.kind == "message": | |
| content = output.text[len(sent_text):] if output.text.startswith(sent_text) else output.text | |
| if not content: | |
| continue | |
| if not sent_role: | |
| sent_role = True | |
| yield completion_chunk(model, {"role": "assistant", "content": content}, None, completion_id, created) | |
| else: | |
| yield completion_chunk(model, {"content": content}, None, completion_id, created) | |
| if not sent_role: | |
| yield completion_chunk(model, {"role": "assistant", "content": ""}, None, completion_id, created) | |
| yield completion_chunk(model, {}, "stop", completion_id, created) | |
| def handle(body: dict[str, Any]) -> dict[str, Any] | Iterator[dict[str, Any]]: | |
| if body.get("stream"): | |
| if is_image_chat_request(body): | |
| return image_chat_events(body) | |
| model, messages = text_chat_parts(body) | |
| return stream_text_chat_completion(text_backend(), messages, model) | |
| if is_image_chat_request(body): | |
| return image_chat_response(body) | |
| model, messages = text_chat_parts(body) | |
| request = ConversationRequest(model=model, messages=messages) | |
| return completion_response(model, collect_text(text_backend(), request), messages=messages) | |