@@ -199,16 +199,52 @@ func TestRunTools_MultiStepToolLoop(t *testing.T) {
199199 }
200200}
201201
202- func TestRunTools_StopsAfterMaxTurns (t * testing.T ) {
202+ func TestRunTools_FinalizesWithoutToolsAfterMaxTurns (t * testing.T ) {
203203 responses := make ([]* openai.ChatCompletionResponse , 0 , toolsMaxTurns )
204204 for i := 0 ; i < toolsMaxTurns ; i ++ {
205205 responses = append (responses , chatResponse (message ("assistant" , "" , []openai.ToolCall {
206206 toolCall ("loop-call" , "search_file" , fmt .Sprintf (`{"query":"x","limit":1,"offset":%d}` , i )),
207207 })))
208208 }
209+ responses = append (responses , chatResponse (message ("assistant" , "Итог после принудительной финализации" , nil )))
209210
210211 client := & scriptedRequester {responses : responses }
211- filePath := writeTempFile (t , "x1\n x2\n x3\n x4\n x5\n x6\n x7\n x8\n x9\n x10\n " )
212+ filePath := writeTempFile (t , strings .Join (buildNumberedLines ("x" , toolsMaxTurns + 5 ), "\n " )+ "\n " )
213+
214+ result , err := Run (context .Background (), client , filePath , "loop?" , nil )
215+ if err != nil {
216+ t .Fatalf ("RunTools() error = %v" , err )
217+ }
218+ if result != "Итог после принудительной финализации" {
219+ t .Fatalf ("RunTools() result = %q, want forced finalization answer" , result )
220+ }
221+ if len (client .requests ) != toolsMaxTurns + 1 {
222+ t .Fatalf ("requests = %d, want %d" , len (client .requests ), toolsMaxTurns + 1 )
223+ }
224+ lastReq := client .requests [len (client .requests )- 1 ]
225+ if lastReq .ToolChoice != "none" {
226+ t .Fatalf ("ToolChoice = %#v, want none" , lastReq .ToolChoice )
227+ }
228+ lastMsg := lastReq .Messages [len (lastReq .Messages )- 1 ]
229+ if ! strings .Contains (lastMsg .Content , "Останови вызовы инструментов" ) {
230+ t .Fatalf ("finalization message = %q, want stop-tools instruction" , lastMsg .Content )
231+ }
232+ }
233+
234+ func TestRunTools_ReturnsOrchestrationLimitWhenForcedFinalizationStillEmpty (t * testing.T ) {
235+ responses := make ([]* openai.ChatCompletionResponse , 0 , toolsMaxTurns + 2 )
236+ for i := 0 ; i < toolsMaxTurns ; i ++ {
237+ responses = append (responses , chatResponse (message ("assistant" , "" , []openai.ToolCall {
238+ toolCall ("loop-call" , "search_file" , fmt .Sprintf (`{"query":"x","limit":1,"offset":%d}` , i )),
239+ })))
240+ }
241+ responses = append (responses ,
242+ chatResponse (message ("assistant" , "" , nil )),
243+ chatResponse (message ("assistant" , "" , nil )),
244+ )
245+
246+ client := & scriptedRequester {responses : responses }
247+ filePath := writeTempFile (t , strings .Join (buildNumberedLines ("x" , toolsMaxTurns + 5 ), "\n " )+ "\n " )
212248
213249 _ , err := Run (context .Background (), client , filePath , "loop?" , nil )
214250 var orchErr orchestrationError
@@ -525,6 +561,14 @@ func toolCall(id string, name string, arguments string) openai.ToolCall {
525561 }
526562}
527563
564+ func buildNumberedLines (prefix string , count int ) []string {
565+ lines := make ([]string , 0 , count )
566+ for i := 1 ; i <= count ; i ++ {
567+ lines = append (lines , fmt .Sprintf ("%s%d" , prefix , i ))
568+ }
569+ return lines
570+ }
571+
528572func writeTempFile (t * testing.T , content string ) string {
529573 t .Helper ()
530574
0 commit comments