Skip to content

Commit da067bc

Browse files
authored
Merge pull request lightspeed-core#418 from tisnik/lcore-464-fix-potential-none-reference-handling
LCORE-464: fix potential None reference handling
2 parents b51226d + b6485e8 commit da067bc

2 files changed

Lines changed: 75 additions & 2 deletions

File tree

src/app/endpoints/query.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ def is_input_shield(shield: Shield) -> bool:
375375
return _is_inout_shield(shield) or not is_output_shield(shield)
376376

377377

378-
async def retrieve_response( # pylint: disable=too-many-locals
378+
async def retrieve_response( # pylint: disable=too-many-locals,too-many-branches
379379
client: AsyncLlamaStackClient,
380380
model_id: str,
381381
query_request: QueryRequest,
@@ -493,7 +493,18 @@ async def retrieve_response( # pylint: disable=too-many-locals
493493
metrics.llm_calls_validation_errors_total.inc()
494494
break
495495

496-
return str(response.output_message.content), conversation_id # type: ignore[union-attr]
496+
output_message = getattr(response, "output_message", None)
497+
if output_message is not None:
498+
content = getattr(output_message, "content", None)
499+
if content is not None:
500+
return str(content), conversation_id
501+
502+
# fallback
503+
logger.warning(
504+
"Response lacks output_message.content (conversation_id=%s)",
505+
conversation_id,
506+
)
507+
return "", conversation_id
497508

498509

499510
def validate_attachments_metadata(attachments: list[Attachment]) -> None:

tests/unit/app/endpoints/test_query.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,68 @@ def test_validate_attachments_metadata_invalid_content_type():
384384
)
385385

386386

387+
@pytest.mark.asyncio
388+
async def test_retrieve_response_no_returned_message(prepare_agent_mocks, mocker):
389+
"""Test the retrieve_response function."""
390+
mock_client, mock_agent = prepare_agent_mocks
391+
mock_agent.create_turn.return_value.output_message = None
392+
mock_client.shields.list.return_value = []
393+
mock_vector_db = mocker.Mock()
394+
mock_vector_db.identifier = "VectorDB-1"
395+
mock_client.vector_dbs.list.return_value = [mock_vector_db]
396+
397+
# Mock configuration with empty MCP servers
398+
mock_config = mocker.Mock()
399+
mock_config.mcp_servers = []
400+
mocker.patch("app.endpoints.query.configuration", mock_config)
401+
mocker.patch(
402+
"app.endpoints.query.get_agent",
403+
return_value=(mock_agent, "fake_conversation_id", "fake_session_id"),
404+
)
405+
406+
query_request = QueryRequest(query="What is OpenStack?")
407+
model_id = "fake_model_id"
408+
access_token = "test_token"
409+
410+
response, _ = await retrieve_response(
411+
mock_client, model_id, query_request, access_token
412+
)
413+
414+
# fallback mechanism: check that the response is empty
415+
assert response == ""
416+
417+
418+
@pytest.mark.asyncio
419+
async def test_retrieve_response_message_without_content(prepare_agent_mocks, mocker):
420+
"""Test the retrieve_response function."""
421+
mock_client, mock_agent = prepare_agent_mocks
422+
mock_agent.create_turn.return_value.output_message.content = None
423+
mock_client.shields.list.return_value = []
424+
mock_vector_db = mocker.Mock()
425+
mock_vector_db.identifier = "VectorDB-1"
426+
mock_client.vector_dbs.list.return_value = [mock_vector_db]
427+
428+
# Mock configuration with empty MCP servers
429+
mock_config = mocker.Mock()
430+
mock_config.mcp_servers = []
431+
mocker.patch("app.endpoints.query.configuration", mock_config)
432+
mocker.patch(
433+
"app.endpoints.query.get_agent",
434+
return_value=(mock_agent, "fake_conversation_id", "fake_session_id"),
435+
)
436+
437+
query_request = QueryRequest(query="What is OpenStack?")
438+
model_id = "fake_model_id"
439+
access_token = "test_token"
440+
441+
response, _ = await retrieve_response(
442+
mock_client, model_id, query_request, access_token
443+
)
444+
445+
# fallback mechanism: check that the response is empty
446+
assert response == ""
447+
448+
387449
@pytest.mark.asyncio
388450
async def test_retrieve_response_vector_db_available(prepare_agent_mocks, mocker):
389451
"""Test the retrieve_response function."""

0 commit comments

Comments
 (0)