mirror of
https://github.com/usestrix/strix.git
synced 2026-08-16 17:27:26 +02:00
test(llm): cover the full run loop against a non-streaming gateway; drop README note
Adds an integration test that drives Runner.run_streamed against a non-streaming gateway through _NonStreamingModel: the synthetic terminal event feeds the runner, which executes the tool call and continues to a final answer over two non-streaming turns. Removes the README env-var note.
This commit is contained in:
@@ -262,7 +262,6 @@ export LLM_API_KEY="your-api-key"
|
||||
export LLM_API_BASE="your-api-base-url" # if using a local model, e.g. Ollama, LMStudio
|
||||
export PERPLEXITY_API_KEY="your-api-key" # for search capabilities
|
||||
export STRIX_REASONING_EFFORT="high" # control thinking effort (default: high, quick scan: medium)
|
||||
export LLM_DISABLE_STREAMING="true" # for OpenAI-compatible endpoints that don't support streaming
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
|
||||
@@ -16,9 +16,11 @@ from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import pytest
|
||||
from agents import Agent, Runner, function_tool
|
||||
from agents.model_settings import ModelSettings
|
||||
from agents.models.interface import Model, ModelTracing
|
||||
from agents.models.interface import Model, ModelProvider, ModelTracing
|
||||
from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
|
||||
from agents.run import RunConfig
|
||||
from openai import AsyncOpenAI, BadRequestError
|
||||
from openai.types.responses import (
|
||||
ResponseCompletedEvent,
|
||||
@@ -205,6 +207,72 @@ async def test_wrapper_get_response_stays_non_streaming(gateway_url: str) -> Non
|
||||
assert tool_call.name == "do_thing"
|
||||
|
||||
|
||||
_TURN_STREAM_FLAGS: list[bool] = []
|
||||
|
||||
|
||||
class _MultiTurnHandler(BaseHTTPRequestHandler):
|
||||
"""Non-streaming gateway: a tool call on turn 1, a final answer on turn 2."""
|
||||
|
||||
def log_message(self, *args: Any) -> None:
|
||||
pass
|
||||
|
||||
def do_POST(self) -> None:
|
||||
length = int(self.headers.get("Content-Length", 0))
|
||||
body = json.loads(self.rfile.read(length) or b"{}")
|
||||
_TURN_STREAM_FLAGS.append(bool(body.get("stream")))
|
||||
completion = _tool_call_completion() if len(_TURN_STREAM_FLAGS) == 1 else _text_completion()
|
||||
if len(_TURN_STREAM_FLAGS) > 1:
|
||||
completion["choices"][0]["message"]["content"] = "all done"
|
||||
payload = json.dumps(completion).encode()
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Content-Length", str(len(payload)))
|
||||
self.end_headers()
|
||||
self.wfile.write(payload)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def multiturn_url() -> Iterator[str]:
|
||||
_TURN_STREAM_FLAGS.clear()
|
||||
server = HTTPServer(("127.0.0.1", 0), _MultiTurnHandler)
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
try:
|
||||
yield f"http://127.0.0.1:{server.server_address[1]}/v1"
|
||||
finally:
|
||||
server.shutdown()
|
||||
server.server_close()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_loop_executes_tool_and_completes_without_streaming(multiturn_url: str) -> None:
|
||||
# The whole streamed agent loop runs against a non-streaming gateway: the
|
||||
# synthetic terminal event feeds the runner, which executes the tool and
|
||||
# continues the turn until a final answer.
|
||||
calls: list[int] = []
|
||||
|
||||
@function_tool
|
||||
def do_thing(n: int) -> str:
|
||||
calls.append(n)
|
||||
return f"did {n}"
|
||||
|
||||
class _Provider(ModelProvider):
|
||||
def get_model(self, model_name: str | None) -> Model: # noqa: ARG002
|
||||
return _NonStreamingModel(_model(multiturn_url))
|
||||
|
||||
agent = Agent(name="t", instructions="use the tool", tools=[do_thing], model="gw-model")
|
||||
result = Runner.run_streamed(
|
||||
agent, input="please", run_config=RunConfig(model_provider=_Provider())
|
||||
)
|
||||
async for _ in result.stream_events():
|
||||
pass
|
||||
|
||||
assert calls == [1] # tool executed with the streamed tool-call args
|
||||
assert result.final_output == "all done"
|
||||
assert len(_TURN_STREAM_FLAGS) == 2 # two turns, both...
|
||||
assert not any(_TURN_STREAM_FLAGS) # ...issued as non-streaming requests
|
||||
|
||||
|
||||
class _DummyModel(Model):
|
||||
async def get_response(self, *args: Any, **kwargs: Any) -> Any:
|
||||
raise NotImplementedError
|
||||
|
||||
Reference in New Issue
Block a user