chwellofficial/nt360Slides
0
1import asyncio2import json3from collections.abc import AsyncGenerator, Sequence4from typing import Any, Optional5 6import dirtyjson7from llmai.shared import (8 LLMTool,9 Message,10 ResponseFormat,11 normalize_content_parts,12)13 14from utils.llm_config import get_extra_body15 16 17def get_generate_kwargs(18 model: str,19 messages: Sequence[Message],20 max_tokens: Optional[int] = None,21 tools: Optional[list[LLMTool]] = None,22 response_format: Optional[ResponseFormat] = None,23 stream: bool = False,24) -> dict[str, Any]:25 kwargs: dict[str, Any] = {26 "model": model,27 "messages": list(messages),28 "stream": stream,29 }30 if max_tokens is not None:31 kwargs["max_tokens"] = max_tokens32 if tools:33 kwargs["tools"] = tools34 if response_format is not None:35 kwargs["response_format"] = response_format36 37 extra_body = get_extra_body()38 if extra_body:39 kwargs["extra_body"] = extra_body40 41 return kwargs42 43 44def extract_text(content: Any) -> Optional[str]:45 if content is None:46 return None47 if isinstance(content, str):48 return content49 if isinstance(content, Sequence) and not isinstance(content, (bytes, bytearray)):50 parts: list[str] = []51 for part in content:52 if isinstance(part, str):53 parts.append(part)54 continue55 text = getattr(part, "text", None)56 if isinstance(text, str):57 parts.append(text)58 joined = "".join(parts)59 return joined or None60 text = getattr(content, "text", None)61 if isinstance(text, str):62 return text63 return None64 65 66def extract_structured_content(content: Any) -> Optional[dict]:67 if content is None:68 return None69 if isinstance(content, dict):70 return content71 if hasattr(content, "model_dump"):72 dumped = content.model_dump(mode="json")73 if isinstance(dumped, dict):74 return dumped75 76 raw_text = extract_text(content)77 if not raw_text:78 return None79 80 try:81 parsed = dirtyjson.loads(raw_text)82 except Exception:83 return None84 85 if isinstance(parsed, dict):86 return dict(parsed)87 return None88 89 90def serialize_structured_content(content: Any) -> Optional[str]:91 parsed = extract_structured_content(content)92 if parsed is not None:93 return json.dumps(parsed, ensure_ascii=False)94 95 raw_text = extract_text(content)96 if raw_text:97 return raw_text98 return None99 100 101def message_content_to_text(content: Sequence[Any] | str | None) -> Optional[str]:102 joined = "".join(103 part.text104 for part in normalize_content_parts(content)105 if isinstance(getattr(part, "text", None), str)106 )107 return joined or None108 109 110async def stream_generate_events(client: Any, **kwargs) -> AsyncGenerator[Any, None]:111 loop = asyncio.get_running_loop()112 queue: asyncio.Queue[Any] = asyncio.Queue()113 sentinel = object()114 115 def worker():116 try:117 for event in client.generate(**kwargs):118 loop.call_soon_threadsafe(queue.put_nowait, event)119 except Exception as exc:120 loop.call_soon_threadsafe(queue.put_nowait, exc)121 finally:122 loop.call_soon_threadsafe(queue.put_nowait, sentinel)123 124 worker_task = asyncio.create_task(asyncio.to_thread(worker))125 try:126 while True:127 item = await queue.get()128 if item is sentinel:129 break130 if isinstance(item, Exception):131 raise item132 yield item133 finally:134 await worker_task135 