diff --git a/src/timmy_serve/app.py b/src/timmy_serve/app.py index b9cb337..8b0013a 100644 --- a/src/timmy_serve/app.py +++ b/src/timmy_serve/app.py @@ -75,6 +75,8 @@ def create_timmy_serve_app() -> FastAPI: @asynccontextmanager async def lifespan(app: FastAPI): logger.info("Timmy Serve starting") + app.state.timmy = create_timmy() + logger.info("Timmy agent cached in app state") yield logger.info("Timmy Serve shutting down") @@ -101,7 +103,7 @@ def create_timmy_serve_app() -> FastAPI: async def serve_chat(request: Request, body: ChatRequest): """Process a chat request.""" try: - timmy = create_timmy() + timmy = request.app.state.timmy result = timmy.run(body.message, stream=False) response_text = result.content if hasattr(result, "content") else str(result) diff --git a/tests/timmy/test_timmy_serve_app.py b/tests/timmy/test_timmy_serve_app.py index 37facd2..c4a9ad3 100644 --- a/tests/timmy/test_timmy_serve_app.py +++ b/tests/timmy/test_timmy_serve_app.py @@ -8,11 +8,14 @@ from fastapi.testclient import TestClient @pytest.fixture def serve_client(): - """Create a TestClient for the timmy-serve app.""" - from timmy_serve.app import create_timmy_serve_app + """Create a TestClient for the timmy-serve app with mocked Timmy agent.""" + with patch("timmy_serve.app.create_timmy") as mock_create: + mock_create.return_value = MagicMock() + from timmy_serve.app import create_timmy_serve_app - app = create_timmy_serve_app() - return TestClient(app) + app = create_timmy_serve_app() + with TestClient(app) as client: + yield client class TestHealthEndpoint: @@ -34,18 +37,40 @@ class TestServeStatus: class TestServeChatEndpoint: @patch("timmy_serve.app.create_timmy") - def test_chat_returns_response(self, mock_create, serve_client): + def test_chat_returns_response(self, mock_create): mock_agent = MagicMock() mock_result = MagicMock() mock_result.content = "I am Timmy." mock_agent.run.return_value = mock_result mock_create.return_value = mock_agent - resp = serve_client.post( - "/serve/chat", - json={"message": "Who are you?"}, - ) + from timmy_serve.app import create_timmy_serve_app + + app = create_timmy_serve_app() + with TestClient(app) as client: + resp = client.post( + "/serve/chat", + json={"message": "Who are you?"}, + ) assert resp.status_code == 200 data = resp.json() assert data["response"] == "I am Timmy." mock_agent.run.assert_called_once_with("Who are you?", stream=False) + + @patch("timmy_serve.app.create_timmy") + def test_agent_cached_at_startup(self, mock_create): + """Verify create_timmy is called once at startup, not per request.""" + mock_agent = MagicMock() + mock_result = MagicMock() + mock_result.content = "reply" + mock_agent.run.return_value = mock_result + mock_create.return_value = mock_agent + + from timmy_serve.app import create_timmy_serve_app + + app = create_timmy_serve_app() + with TestClient(app) as client: + # Two requests — create_timmy should only be called once (at startup) + client.post("/serve/chat", json={"message": "hello"}) + client.post("/serve/chat", json={"message": "world"}) + mock_create.assert_called_once()