@@ -352,3 +352,118 @@ def test_run_agentic_conversation_treats_alert_proposal_as_a_clarification():
352352 assert "Should I create this alert?" in mock_sim .call_args .args [0 ]
353353 assert result .turn_results [0 ].clarification_turns_used == 1
354354 assert result .turn_results [0 ].skill_success is True
355+
356+
357+ def _viz_turn_result (text = None , viz = None , tool_calls = ()):
358+ r = MagicMock ()
359+ r .text_response = text
360+ r .created_visualizations = viz
361+ r .tool_call_events = list (tool_calls )
362+ r .alert_proposals = []
363+ return r
364+
365+
366+ def test_run_agentic_conversation_replies_to_a_statement_without_a_question_mark ():
367+ """QA-28982 regression: gpt-5.2 answered "I need to confirm ... Next I'll:" -- no question
368+ mark, so the old substring heuristic ended the turn and no metric was ever created."""
369+ mock_client = MagicMock ()
370+ mock_client .create_conversation .return_value = "conv-1"
371+ stalling_turn = _viz_turn_result (
372+ text = "I can create that, but first I need to confirm which Net Sales calculation to use. Next I'll: ..." ,
373+ tool_calls = [_skills_tc ("metric" )],
374+ )
375+ mock_client .send_message .side_effect = [
376+ stalling_turn ,
377+ _metric_turn_result ([_skills_tc ("metric" ), _create_metric_tc ("m1" )]),
378+ ]
379+
380+ with (
381+ patch ("gooddata_eval.core.agentic.conversation.ChatClient" , return_value = mock_client ),
382+ patch ("gooddata_eval.core.agentic.conversation.GoodDataSdk" ),
383+ patch (
384+ "gooddata_eval.core.agentic.conversation._get_sim_user_response" ,
385+ return_value = "Go ahead with Net Sales." ,
386+ ) as mock_sim ,
387+ ):
388+ result = run_agentic_conversation (
389+ host = "http://host/api/v1/actions/workspaces/ws1/ai" ,
390+ token = "tok" ,
391+ workspace_id = "ws1" ,
392+ fixture = _two_metric_turn_fixture ().model_copy (update = {"turns" : _two_metric_turn_fixture ().turns [:1 ]}),
393+ )
394+
395+ mock_sim .assert_called_once ()
396+ assert result .turn_results [0 ].clarification_turns_used == 1
397+ assert result .turn_results [0 ].skill_success is True
398+
399+
400+ def test_run_agentic_conversation_stops_when_the_agent_says_nothing ():
401+ """An agent that returns neither text nor tool calls is stuck -- no point replying to it."""
402+ mock_client = MagicMock ()
403+ mock_client .create_conversation .return_value = "conv-1"
404+ mock_client .send_message .return_value = _viz_turn_result (text = None )
405+
406+ with (
407+ patch ("gooddata_eval.core.agentic.conversation.ChatClient" , return_value = mock_client ),
408+ patch ("gooddata_eval.core.agentic.conversation.GoodDataSdk" ),
409+ patch ("gooddata_eval.core.agentic.conversation._get_sim_user_response" ) as mock_sim ,
410+ ):
411+ result = run_agentic_conversation (
412+ host = "http://host/api/v1/actions/workspaces/ws1/ai" ,
413+ token = "tok" ,
414+ workspace_id = "ws1" ,
415+ fixture = _two_metric_turn_fixture ().model_copy (update = {"turns" : _two_metric_turn_fixture ().turns [:1 ]}),
416+ )
417+
418+ mock_sim .assert_not_called ()
419+ assert mock_client .send_message .call_count == 1
420+ assert result .turn_results [0 ].skill_success is False
421+
422+
423+ def test_run_agentic_conversation_records_a_failed_turn_when_a_ref_cannot_be_resolved ():
424+ """QA-28982 regression: turn 1 producing no metric used to raise ValueError out of the whole
425+ run, hiding which turn broke and skipping every later turn."""
426+ mock_client = MagicMock ()
427+ mock_client .create_conversation .return_value = "conv-1"
428+ mock_client .send_message .side_effect = [
429+ _viz_turn_result (text = "Which Net Sales metric?" , tool_calls = [_skills_tc ("metric" )]),
430+ _viz_turn_result (text = "Working on it." , tool_calls = [_skills_tc ("metric" )]),
431+ _metric_turn_result ([_skills_tc ("metric" ), _create_metric_tc ("m2" )]),
432+ ]
433+ fixture = ConversationFixture (
434+ id = "test-ref" ,
435+ expected_skills = ["metric" ],
436+ turns = [
437+ TurnDefinition (
438+ turn_id = "t1" , message = "Create shared" , expected_skill = "metric" , expected_output_type = "metric"
439+ ),
440+ TurnDefinition (
441+ turn_id = "t2" ,
442+ message = "Chart it" ,
443+ expected_skill = "visualization" ,
444+ expected_output = {"metrics" : ["metric/$ref:t1.metric_id" ]},
445+ ),
446+ TurnDefinition (
447+ turn_id = "t3" , message = "Create another" , expected_skill = "metric" , expected_output_type = "metric"
448+ ),
449+ ],
450+ )
451+
452+ with (
453+ patch ("gooddata_eval.core.agentic.conversation.ChatClient" , return_value = mock_client ),
454+ patch ("gooddata_eval.core.agentic.conversation.GoodDataSdk" ),
455+ patch ("gooddata_eval.core.agentic.conversation._get_sim_user_response" , return_value = "Go ahead." ),
456+ ):
457+ result = run_agentic_conversation (
458+ host = "http://host/api/v1/actions/workspaces/ws1/ai" ,
459+ token = "tok" ,
460+ workspace_id = "ws1" ,
461+ fixture = fixture ,
462+ max_clarification_turns = 1 ,
463+ )
464+
465+ assert [t .turn_id for t in result .turn_results ] == ["t1" , "t2" , "t3" ]
466+ assert result .turn_results [0 ].skill_success is False
467+ assert result .turn_results [1 ].no_error is False
468+ assert result .turn_results [2 ].skill_success is True
469+ assert result .conversation_success is False
0 commit comments