bug fix sending component
This commit is contained in:
@@ -233,10 +233,19 @@ async def _handle_home_request(
|
||||
executor = await _make_ws_executor(websocket, user_id)
|
||||
set_client_executor(executor)
|
||||
response_chunks: list[str] = []
|
||||
agent_holder: list = []
|
||||
try:
|
||||
token_stream = orchestrate_v3_stream(user_id, message, context)
|
||||
token_stream = orchestrate_v3_stream(
|
||||
user_id, message, context, agent_holder=agent_holder
|
||||
)
|
||||
formatter = HomeFormatter(request_id=request_id, tool_results=[])
|
||||
async for ws_frame in formatter.format(token_stream):
|
||||
# Inject mutations from agent tool_results into stream_end
|
||||
if ws_frame.type == "stream_end" and agent_holder: # type: ignore[union-attr]
|
||||
ws_frame.mutations = [ # type: ignore[union-attr]
|
||||
{"action": r["action"], "table": r["table"], "data": r["data"]}
|
||||
for r in getattr(agent_holder[0], "tool_results", [])
|
||||
]
|
||||
await websocket.send_text(ws_frame.model_dump_json())
|
||||
# Collect text chunks to build the full response for episode storage
|
||||
if ws_frame.type == "stream_text": # type: ignore[union-attr]
|
||||
@@ -278,10 +287,18 @@ async def _handle_floating_request(
|
||||
executor = await _make_ws_executor(websocket, user_id)
|
||||
set_client_executor(executor)
|
||||
response_chunks: list[str] = []
|
||||
agent_holder: list = []
|
||||
try:
|
||||
token_stream = orchestrate_v3_stream(user_id, message, context)
|
||||
token_stream = orchestrate_v3_stream(
|
||||
user_id, message, context, agent_holder=agent_holder
|
||||
)
|
||||
formatter = FloatingFormatter(request_id=request_id)
|
||||
async for ws_frame in formatter.format(token_stream):
|
||||
if ws_frame.type == "stream_end" and agent_holder: # type: ignore[union-attr]
|
||||
ws_frame.mutations = [ # type: ignore[union-attr]
|
||||
{"action": r["action"], "table": r["table"], "data": r["data"]}
|
||||
for r in getattr(agent_holder[0], "tool_results", [])
|
||||
]
|
||||
await websocket.send_text(ws_frame.model_dump_json())
|
||||
if ws_frame.type == "stream_text": # type: ignore[union-attr]
|
||||
response_chunks.append(ws_frame.chunk) # type: ignore[union-attr]
|
||||
|
||||
@@ -23,10 +23,15 @@ from openai import AsyncOpenAI
|
||||
import litellm
|
||||
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langchain_litellm import ChatLiteLLM
|
||||
from litellm import get_supported_openai_params # noqa: F401 – validates install
|
||||
|
||||
from app.config.settings import settings
|
||||
|
||||
# Some models (e.g. gpt-5, o-series) reject unsupported params like temperature.
|
||||
# Drop them silently instead of raising UnsupportedParamsError.
|
||||
litellm.drop_params = True
|
||||
|
||||
|
||||
def _api_key_for_model(model: str) -> str | None:
|
||||
"""Return the most appropriate API key for the given LiteLLM model string."""
|
||||
@@ -48,7 +53,7 @@ def get_llm(
|
||||
*,
|
||||
model: str | None = None,
|
||||
temperature: float = 0,
|
||||
) -> ChatOpenAI:
|
||||
) -> ChatOpenAI | ChatLiteLLM:
|
||||
"""Return a LangChain chat model backed by LiteLLM.
|
||||
|
||||
LiteLLM exposes an OpenAI-compatible API, so we use ``ChatOpenAI`` pointed
|
||||
@@ -69,6 +74,11 @@ def get_llm(
|
||||
if settings.GITHUB_COPILOT_TOKEN_DIR:
|
||||
os.environ.setdefault("GITHUB_COPILOT_TOKEN_DIR", settings.GITHUB_COPILOT_TOKEN_DIR)
|
||||
|
||||
# Use ChatLiteLLM for provider-prefixed models (github_copilot/, anthropic/, etc.)
|
||||
# so LiteLLM handles routing and auth. ChatOpenAI for plain OpenAI model names.
|
||||
if "/" in model:
|
||||
return ChatLiteLLM(model=model, temperature=temperature)
|
||||
|
||||
return ChatOpenAI(
|
||||
model=model,
|
||||
temperature=temperature,
|
||||
@@ -79,7 +89,7 @@ def get_llm(
|
||||
def get_router_llm(
|
||||
*,
|
||||
temperature: float = 0,
|
||||
) -> ChatOpenAI:
|
||||
) -> ChatOpenAI | ChatLiteLLM:
|
||||
"""Return the lighter model used for intent classification / routing."""
|
||||
return get_llm(model=settings.LLM_ROUTER_MODEL, temperature=temperature)
|
||||
|
||||
|
||||
@@ -162,17 +162,23 @@ async def orchestrate_v3_stream(
|
||||
message: str,
|
||||
context: dict[str, Any],
|
||||
reg: AgentRegistry | None = None,
|
||||
agent_holder: list | None = None,
|
||||
) -> AsyncGenerator[tuple[str, str], None]:
|
||||
"""v3 streaming orchestration — yields (agent_name, token) pairs.
|
||||
|
||||
The first yield always carries the agent_name with an empty token so that
|
||||
callers (e.g. FloatingFormatter) can detect the routing domain before any text
|
||||
tokens arrive.
|
||||
|
||||
If *agent_holder* is provided (a list), the agent instance is appended so
|
||||
callers can access ``agent.tool_results`` after the stream completes.
|
||||
"""
|
||||
if reg is None:
|
||||
reg = _default_registry
|
||||
agent_name = await classify_intent(message, context, reg)
|
||||
agent = reg.get(agent_name)
|
||||
if agent_holder is not None:
|
||||
agent_holder.append(agent)
|
||||
yield agent_name, "" # domain signal — no token yet
|
||||
async for token in agent.handle_stream(message, context):
|
||||
yield agent_name, token
|
||||
|
||||
@@ -84,5 +84,9 @@ async def execute_on_client(
|
||||
result = await callback(payload)
|
||||
collector = _tool_result_collector.get(None)
|
||||
if collector is not None:
|
||||
collector.append(result)
|
||||
collector.append({
|
||||
"action": action,
|
||||
"table": table,
|
||||
"data": result,
|
||||
})
|
||||
return result
|
||||
|
||||
@@ -24,7 +24,7 @@ from app.config.settings import settings
|
||||
engine = create_async_engine(
|
||||
settings.DATABASE_URL,
|
||||
pool_pre_ping=True,
|
||||
echo=settings.ENV == "dev",
|
||||
echo=False,
|
||||
)
|
||||
|
||||
async_session = async_sessionmaker(engine, expire_on_commit=False)
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
from contextlib import asynccontextmanager
|
||||
import logging
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
||||
)
|
||||
logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)
|
||||
logging.getLogger("sqlalchemy.pool").setLevel(logging.WARNING)
|
||||
|
||||
from app.api.middleware.rate_limit import TierRateLimitMiddleware
|
||||
from app.api.middleware.sanitizer import SanitizerMiddleware
|
||||
from app.config.settings import settings
|
||||
|
||||
Reference in New Issue
Block a user