Spaces:
Running
Running
| """Main chat agent via gemini-3.1-flash-lite (LangChain; Streamlit-safe).""" | |
| from __future__ import annotations | |
| import time | |
| from collections.abc import Iterator | |
| from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, SystemMessage | |
| from agent.llm import get_chat_llm, message_text | |
| from agent.state import ChatTurn | |
| from config import CHAT_SYSTEM_PROMPT | |
| # Soft-chunk size so Streamlit write_stream paints gradually even when the | |
| # model returns large deltas. | |
| _STREAM_CHUNK_CHARS = 8 | |
| _STREAM_CHUNK_PAUSE_S = 0.015 | |
| def _to_lc_messages(turns: list[ChatTurn]) -> list[BaseMessage]: | |
| messages: list[BaseMessage] = [ | |
| SystemMessage(content=CHAT_SYSTEM_PROMPT), | |
| ] | |
| for turn in turns: | |
| if turn["role"] == "user": | |
| messages.append(HumanMessage(content=turn["content"])) | |
| else: | |
| messages.append(AIMessage(content=turn["content"])) | |
| return messages | |
| def _soft_chunks(text: str) -> Iterator[str]: | |
| step = _STREAM_CHUNK_CHARS | |
| for i in range(0, len(text), step): | |
| yield text[i : i + step] | |
| if _STREAM_CHUNK_PAUSE_S: | |
| time.sleep(_STREAM_CHUNK_PAUSE_S) | |
| def chat_reply_stream(turns: list[ChatTurn]) -> Iterator[str]: | |
| """Yield reply chunks. Uses LangChain streaming (avoids genai Client close bugs).""" | |
| if not turns: | |
| raise ValueError("empty chat history") | |
| llm = get_chat_llm(temperature=0.7) | |
| try: | |
| for chunk in llm.stream(_to_lc_messages(turns)): | |
| text = message_text(chunk) | |
| if text: | |
| yield from _soft_chunks(text) | |
| except Exception: | |
| # Fallback: one-shot call, then soft-chunk for write_stream UX | |
| full = message_text(llm.invoke(_to_lc_messages(turns))).strip() | |
| if not full: | |
| raise | |
| yield from _soft_chunks(full) | |
| def chat_reply(turns: list[ChatTurn]) -> str: | |
| """Non-streaming helper.""" | |
| if not turns: | |
| raise ValueError("empty chat history") | |
| llm = get_chat_llm(temperature=0.7) | |
| text = message_text(llm.invoke(_to_lc_messages(turns))).strip() | |
| if not text: | |
| raise RuntimeError("empty model response") | |
| return text | |