llama-index-llms-openai 0.6.4__py3-none-any.whl → 0.6.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,6 +43,8 @@ from llama_index.core.base.llms.types import (
43
43
  CompletionResponseGen,
44
44
  LLMMetadata,
45
45
  MessageRole,
46
+ ToolCallBlock,
47
+ TextBlock,
46
48
  )
47
49
  from llama_index.core.bridge.pydantic import (
48
50
  Field,
@@ -121,9 +123,15 @@ class Tokenizer(Protocol):
121
123
 
122
124
 
123
125
  def force_single_tool_call(response: ChatResponse) -> None:
124
- tool_calls = response.message.additional_kwargs.get("tool_calls", [])
126
+ tool_calls = [
127
+ block for block in response.message.blocks if isinstance(block, ToolCallBlock)
128
+ ]
125
129
  if len(tool_calls) > 1:
126
- response.message.additional_kwargs["tool_calls"] = [tool_calls[0]]
130
+ response.message.blocks = [
131
+ block
132
+ for block in response.message.blocks
133
+ if not isinstance(block, ToolCallBlock)
134
+ ] + [tool_calls[0]]
127
135
 
128
136
 
129
137
  class OpenAI(FunctionCallingLLM):
@@ -528,6 +536,7 @@ class OpenAI(FunctionCallingLLM):
528
536
  messages=message_dicts,
529
537
  **self._get_model_kwargs(stream=True, **kwargs),
530
538
  ):
539
+ blocks = []
531
540
  response = cast(ChatCompletionChunk, response)
532
541
  if len(response.choices) > 0:
533
542
  delta = response.choices[0].delta
@@ -545,17 +554,27 @@ class OpenAI(FunctionCallingLLM):
545
554
  role = delta.role or MessageRole.ASSISTANT
546
555
  content_delta = delta.content or ""
547
556
  content += content_delta
557
+ blocks.append(TextBlock(text=content))
548
558
 
549
559
  additional_kwargs = {}
550
560
  if is_function:
551
561
  tool_calls = update_tool_calls(tool_calls, delta.tool_calls)
552
562
  if tool_calls:
553
563
  additional_kwargs["tool_calls"] = tool_calls
564
+ for tool_call in tool_calls:
565
+ if tool_call.function:
566
+ blocks.append(
567
+ ToolCallBlock(
568
+ tool_call_id=tool_call.id,
569
+ tool_kwargs=tool_call.function.arguments or {},
570
+ tool_name=tool_call.function.name or "",
571
+ )
572
+ )
554
573
 
555
574
  yield ChatResponse(
556
575
  message=ChatMessage(
557
576
  role=role,
558
- content=content,
577
+ blocks=blocks,
559
578
  additional_kwargs=additional_kwargs,
560
579
  ),
561
580
  delta=content_delta,
@@ -785,6 +804,7 @@ class OpenAI(FunctionCallingLLM):
785
804
  messages=message_dicts,
786
805
  **self._get_model_kwargs(stream=True, **kwargs),
787
806
  ):
807
+ blocks = []
788
808
  response = cast(ChatCompletionChunk, response)
789
809
  if len(response.choices) > 0:
790
810
  # check if the first chunk has neither content nor tool_calls
@@ -812,17 +832,27 @@ class OpenAI(FunctionCallingLLM):
812
832
  role = delta.role or MessageRole.ASSISTANT
813
833
  content_delta = delta.content or ""
814
834
  content += content_delta
835
+ blocks.append(TextBlock(text=content))
815
836
 
816
837
  additional_kwargs = {}
817
838
  if is_function:
818
839
  tool_calls = update_tool_calls(tool_calls, delta.tool_calls)
819
840
  if tool_calls:
820
841
  additional_kwargs["tool_calls"] = tool_calls
842
+ for tool_call in tool_calls:
843
+ if tool_call.function:
844
+ blocks.append(
845
+ ToolCallBlock(
846
+ tool_call_id=tool_call.id,
847
+ tool_kwargs=tool_call.function.arguments or {},
848
+ tool_name=tool_call.function.name or "",
849
+ )
850
+ )
821
851
 
822
852
  yield ChatResponse(
823
853
  message=ChatMessage(
824
854
  role=role,
825
- content=content,
855
+ blocks=blocks,
826
856
  additional_kwargs=additional_kwargs,
827
857
  ),
828
858
  delta=content_delta,
@@ -960,36 +990,71 @@ class OpenAI(FunctionCallingLLM):
960
990
  **kwargs: Any,
961
991
  ) -> List[ToolSelection]:
962
992
  """Predict and call the tool."""
963
- tool_calls = response.message.additional_kwargs.get("tool_calls", [])
964
-
965
- if len(tool_calls) < 1:
966
- if error_on_no_tool_call:
967
- raise ValueError(
968
- f"Expected at least one tool call, but got {len(tool_calls)} tool calls."
993
+ tool_calls = [
994
+ block
995
+ for block in response.message.blocks
996
+ if isinstance(block, ToolCallBlock)
997
+ ]
998
+ if tool_calls:
999
+ if len(tool_calls) < 1:
1000
+ if error_on_no_tool_call:
1001
+ raise ValueError(
1002
+ f"Expected at least one tool call, but got {len(tool_calls)} tool calls."
1003
+ )
1004
+ else:
1005
+ return []
1006
+
1007
+ tool_selections = []
1008
+ for tool_call in tool_calls:
1009
+ # this should handle both complete and partial jsons
1010
+ try:
1011
+ if isinstance(tool_call.tool_kwargs, str):
1012
+ argument_dict = parse_partial_json(tool_call.tool_kwargs)
1013
+ else:
1014
+ argument_dict = tool_call.tool_kwargs
1015
+ except (ValueError, TypeError, JSONDecodeError):
1016
+ argument_dict = {}
1017
+
1018
+ tool_selections.append(
1019
+ ToolSelection(
1020
+ tool_id=tool_call.tool_call_id or "",
1021
+ tool_name=tool_call.tool_name,
1022
+ tool_kwargs=argument_dict,
1023
+ )
969
1024
  )
970
- else:
971
- return []
972
1025
 
973
- tool_selections = []
974
- for tool_call in tool_calls:
975
- if tool_call.type != "function":
976
- raise ValueError("Invalid tool type. Unsupported by OpenAI llm")
1026
+ return tool_selections
1027
+ else: # keep it backward-compatible
1028
+ tool_calls = response.message.additional_kwargs.get("tool_calls", [])
977
1029
 
978
- # this should handle both complete and partial jsons
979
- try:
980
- argument_dict = parse_partial_json(tool_call.function.arguments)
981
- except (ValueError, TypeError, JSONDecodeError):
982
- argument_dict = {}
983
-
984
- tool_selections.append(
985
- ToolSelection(
986
- tool_id=tool_call.id,
987
- tool_name=tool_call.function.name,
988
- tool_kwargs=argument_dict,
1030
+ if len(tool_calls) < 1:
1031
+ if error_on_no_tool_call:
1032
+ raise ValueError(
1033
+ f"Expected at least one tool call, but got {len(tool_calls)} tool calls."
1034
+ )
1035
+ else:
1036
+ return []
1037
+
1038
+ tool_selections = []
1039
+ for tool_call in tool_calls:
1040
+ if tool_call.type != "function":
1041
+ raise ValueError("Invalid tool type. Unsupported by OpenAI llm")
1042
+
1043
+ # this should handle both complete and partial jsons
1044
+ try:
1045
+ argument_dict = parse_partial_json(tool_call.function.arguments)
1046
+ except (ValueError, TypeError, JSONDecodeError):
1047
+ argument_dict = {}
1048
+
1049
+ tool_selections.append(
1050
+ ToolSelection(
1051
+ tool_id=tool_call.id,
1052
+ tool_name=tool_call.function.name,
1053
+ tool_kwargs=argument_dict,
1054
+ )
989
1055
  )
990
- )
991
1056
 
992
- return tool_selections
1057
+ return tool_selections
993
1058
 
994
1059
  def _prepare_schema(
995
1060
  self, llm_kwargs: Optional[Dict[str, Any]], output_cls: Type[Model]
@@ -44,6 +44,7 @@ from typing import (
44
44
  Type,
45
45
  Union,
46
46
  runtime_checkable,
47
+ cast,
47
48
  )
48
49
 
49
50
  import llama_index.core.instrumentation as instrument
@@ -67,6 +68,7 @@ from llama_index.core.base.llms.types import (
67
68
  TextBlock,
68
69
  ImageBlock,
69
70
  ThinkingBlock,
71
+ ToolCallBlock,
70
72
  )
71
73
  from llama_index.core.bridge.pydantic import (
72
74
  Field,
@@ -131,9 +133,15 @@ class Tokenizer(Protocol):
131
133
 
132
134
 
133
135
  def force_single_tool_call(response: ChatResponse) -> None:
134
- tool_calls = response.message.additional_kwargs.get("tool_calls", [])
136
+ tool_calls = [
137
+ block for block in response.message.blocks if isinstance(block, ToolCallBlock)
138
+ ]
135
139
  if len(tool_calls) > 1:
136
- response.message.additional_kwargs["tool_calls"] = [tool_calls[0]]
140
+ response.message.blocks = [
141
+ block
142
+ for block in response.message.blocks
143
+ if not isinstance(block, ToolCallBlock)
144
+ ] + [tool_calls[0]]
137
145
 
138
146
 
139
147
  class OpenAIResponses(FunctionCallingLLM):
@@ -454,7 +462,6 @@ class OpenAIResponses(FunctionCallingLLM):
454
462
  def _parse_response_output(output: List[ResponseOutputItem]) -> ChatResponse:
455
463
  message = ChatMessage(role=MessageRole.ASSISTANT, blocks=[])
456
464
  additional_kwargs = {"built_in_tool_calls": []}
457
- tool_calls = []
458
465
  blocks: List[ContentBlock] = []
459
466
  for item in output:
460
467
  if isinstance(item, ResponseOutputMessage):
@@ -481,7 +488,13 @@ class OpenAIResponses(FunctionCallingLLM):
481
488
  elif isinstance(item, ResponseFileSearchToolCall):
482
489
  additional_kwargs["built_in_tool_calls"].append(item)
483
490
  elif isinstance(item, ResponseFunctionToolCall):
484
- tool_calls.append(item)
491
+ message.blocks.append(
492
+ ToolCallBlock(
493
+ tool_name=item.name,
494
+ tool_call_id=item.call_id,
495
+ tool_kwargs=item.arguments,
496
+ )
497
+ )
485
498
  elif isinstance(item, ResponseFunctionWebSearch):
486
499
  additional_kwargs["built_in_tool_calls"].append(item)
487
500
  elif isinstance(item, ResponseComputerToolCall):
@@ -504,9 +517,6 @@ class OpenAIResponses(FunctionCallingLLM):
504
517
  )
505
518
  )
506
519
 
507
- if tool_calls and message:
508
- message.additional_kwargs["tool_calls"] = tool_calls
509
-
510
520
  return ChatResponse(message=message, additional_kwargs=additional_kwargs)
511
521
 
512
522
  @llm_retry_decorator
@@ -542,7 +552,6 @@ class OpenAIResponses(FunctionCallingLLM):
542
552
  @staticmethod
543
553
  def process_response_event(
544
554
  event: ResponseStreamEvent,
545
- tool_calls: List[ResponseFunctionToolCall],
546
555
  built_in_tool_calls: List[Any],
547
556
  additional_kwargs: Dict[str, Any],
548
557
  current_tool_call: Optional[ResponseFunctionToolCall],
@@ -550,7 +559,6 @@ class OpenAIResponses(FunctionCallingLLM):
550
559
  previous_response_id: Optional[str] = None,
551
560
  ) -> Tuple[
552
561
  List[ContentBlock],
553
- List[ResponseFunctionToolCall],
554
562
  List[Any],
555
563
  Dict[str, Any],
556
564
  Optional[ResponseFunctionToolCall],
@@ -591,6 +599,7 @@ class OpenAIResponses(FunctionCallingLLM):
591
599
  elif isinstance(event, ResponseTextDeltaEvent):
592
600
  # Text content is being added
593
601
  delta = event.delta
602
+ blocks.append(TextBlock(text=delta))
594
603
  elif isinstance(event, ResponseImageGenCallPartialImageEvent):
595
604
  # Partial image
596
605
  if event.partial_image_b64:
@@ -609,10 +618,12 @@ class OpenAIResponses(FunctionCallingLLM):
609
618
  if current_tool_call is not None:
610
619
  current_tool_call.arguments = event.arguments
611
620
  current_tool_call.status = "completed"
612
-
613
- # append a copy of the tool call to the list
614
- tool_calls.append(
615
- ResponseFunctionToolCall(**current_tool_call.model_dump())
621
+ blocks.append(
622
+ ToolCallBlock(
623
+ tool_name=current_tool_call.name,
624
+ tool_kwargs=current_tool_call.arguments,
625
+ tool_call_id=current_tool_call.call_id,
626
+ )
616
627
  )
617
628
 
618
629
  # clear the current tool call
@@ -658,7 +669,6 @@ class OpenAIResponses(FunctionCallingLLM):
658
669
 
659
670
  return (
660
671
  blocks,
661
- tool_calls,
662
672
  built_in_tool_calls,
663
673
  additional_kwargs,
664
674
  current_tool_call,
@@ -677,7 +687,6 @@ class OpenAIResponses(FunctionCallingLLM):
677
687
  )
678
688
 
679
689
  def gen() -> ChatResponseGen:
680
- tool_calls = []
681
690
  built_in_tool_calls = []
682
691
  additional_kwargs = {"built_in_tool_calls": []}
683
692
  current_tool_call: Optional[ResponseFunctionToolCall] = None
@@ -691,7 +700,6 @@ class OpenAIResponses(FunctionCallingLLM):
691
700
  # Process the event and update state
692
701
  (
693
702
  blocks,
694
- tool_calls,
695
703
  built_in_tool_calls,
696
704
  additional_kwargs,
697
705
  current_tool_call,
@@ -699,7 +707,6 @@ class OpenAIResponses(FunctionCallingLLM):
699
707
  delta,
700
708
  ) = OpenAIResponses.process_response_event(
701
709
  event=event,
702
- tool_calls=tool_calls,
703
710
  built_in_tool_calls=built_in_tool_calls,
704
711
  additional_kwargs=additional_kwargs,
705
712
  current_tool_call=current_tool_call,
@@ -721,9 +728,6 @@ class OpenAIResponses(FunctionCallingLLM):
721
728
  message=ChatMessage(
722
729
  role=MessageRole.ASSISTANT,
723
730
  blocks=blocks,
724
- additional_kwargs={"tool_calls": tool_calls}
725
- if tool_calls
726
- else {},
727
731
  ),
728
732
  delta=delta,
729
733
  raw=event,
@@ -801,7 +805,6 @@ class OpenAIResponses(FunctionCallingLLM):
801
805
  )
802
806
 
803
807
  async def gen() -> ChatResponseAsyncGen:
804
- tool_calls = []
805
808
  built_in_tool_calls = []
806
809
  additional_kwargs = {"built_in_tool_calls": []}
807
810
  current_tool_call: Optional[ResponseFunctionToolCall] = None
@@ -817,7 +820,6 @@ class OpenAIResponses(FunctionCallingLLM):
817
820
  # Process the event and update state
818
821
  (
819
822
  blocks,
820
- tool_calls,
821
823
  built_in_tool_calls,
822
824
  additional_kwargs,
823
825
  current_tool_call,
@@ -825,7 +827,6 @@ class OpenAIResponses(FunctionCallingLLM):
825
827
  delta,
826
828
  ) = OpenAIResponses.process_response_event(
827
829
  event=event,
828
- tool_calls=tool_calls,
829
830
  built_in_tool_calls=built_in_tool_calls,
830
831
  additional_kwargs=additional_kwargs,
831
832
  current_tool_call=current_tool_call,
@@ -847,9 +848,6 @@ class OpenAIResponses(FunctionCallingLLM):
847
848
  message=ChatMessage(
848
849
  role=MessageRole.ASSISTANT,
849
850
  blocks=blocks,
850
- additional_kwargs={"tool_calls": tool_calls}
851
- if tool_calls
852
- else {},
853
851
  ),
854
852
  delta=delta,
855
853
  raw=event,
@@ -915,9 +913,11 @@ class OpenAIResponses(FunctionCallingLLM):
915
913
  **kwargs: Any,
916
914
  ) -> List[ToolSelection]:
917
915
  """Predict and call the tool."""
918
- tool_calls: List[ResponseFunctionToolCall] = (
919
- response.message.additional_kwargs.get("tool_calls", [])
920
- )
916
+ tool_calls: List[ToolCallBlock] = [
917
+ block
918
+ for block in response.message.blocks
919
+ if isinstance(block, ToolCallBlock)
920
+ ]
921
921
 
922
922
  if len(tool_calls) < 1:
923
923
  if error_on_no_tool_call:
@@ -931,14 +931,14 @@ class OpenAIResponses(FunctionCallingLLM):
931
931
  for tool_call in tool_calls:
932
932
  # this should handle both complete and partial jsons
933
933
  try:
934
- argument_dict = parse_partial_json(tool_call.arguments)
935
- except ValueError:
934
+ argument_dict = parse_partial_json(cast(str, tool_call.tool_kwargs))
935
+ except Exception:
936
936
  argument_dict = {}
937
937
 
938
938
  tool_selections.append(
939
939
  ToolSelection(
940
- tool_id=tool_call.call_id,
941
- tool_name=tool_call.name,
940
+ tool_id=tool_call.tool_call_id or "",
941
+ tool_name=tool_call.tool_name,
942
942
  tool_kwargs=argument_dict,
943
943
  )
944
944
  )
@@ -30,6 +30,8 @@ from llama_index.core.base.llms.types import (
30
30
  AudioBlock,
31
31
  DocumentBlock,
32
32
  ThinkingBlock,
33
+ ToolCallBlock,
34
+ ContentBlock,
33
35
  )
34
36
  from llama_index.core.bridge.pydantic import BaseModel
35
37
 
@@ -61,7 +63,6 @@ O1_MODELS: Dict[str, int] = {
61
63
  "gpt-5-mini-2025-08-07": 400000,
62
64
  "gpt-5-nano": 400000,
63
65
  "gpt-5-nano-2025-08-07": 400000,
64
- "gpt-5-chat-latest": 400000,
65
66
  "gpt-5-pro": 400000,
66
67
  "gpt-5-pro-2025-10-06": 400000,
67
68
  }
@@ -117,6 +118,8 @@ GPT4_MODELS: Dict[str, int] = {
117
118
  "gpt-4.1-2025-04-14": 1047576,
118
119
  "gpt-4.1-mini-2025-04-14": 1047576,
119
120
  "gpt-4.1-nano-2025-04-14": 1047576,
121
+ # Latest GPT-5-chat supports setting temperature, so putting it here
122
+ "gpt-5-chat-latest": 128000,
120
123
  }
121
124
 
122
125
  AZURE_TURBO_MODELS: Dict[str, int] = {
@@ -398,6 +401,30 @@ def to_openai_message_dict(
398
401
  },
399
402
  }
400
403
  )
404
+ elif isinstance(block, ToolCallBlock):
405
+ try:
406
+ function_dict = {
407
+ "type": "function",
408
+ "function": {
409
+ "name": block.tool_name,
410
+ "arguments": block.tool_kwargs,
411
+ },
412
+ "id": block.tool_call_id,
413
+ }
414
+
415
+ if len(content) == 0 or content[-1]["type"] != "text":
416
+ content.append(
417
+ {"type": "text", "text": "", "tool_calls": [function_dict]}
418
+ )
419
+ elif content[-1]["type"] == "text" and "tool_calls" in content[-1]:
420
+ content[-1]["tool_calls"].append(function_dict)
421
+ elif content[-1]["type"] == "text" and "tool_calls" not in content[-1]:
422
+ content[-1]["tool_calls"] = [function_dict]
423
+ except Exception:
424
+ logger.warning(
425
+ f"It was not possible to convert ToolCallBlock with call id {block.tool_call_id or '`no call id`'} to a valid message, skipping..."
426
+ )
427
+ continue
401
428
  else:
402
429
  msg = f"Unsupported content block type: {type(block).__name__}"
403
430
  raise ValueError(msg)
@@ -405,6 +432,9 @@ def to_openai_message_dict(
405
432
  # NOTE: Sending a null value (None) for Tool Message to OpenAI will cause error
406
433
  # It's only Allowed to send None if it's an Assistant Message and either a function call or tool calls were performed
407
434
  # Reference: https://platform.openai.com/docs/api-reference/chat/create
435
+ already_has_tool_calls = any(
436
+ isinstance(block, ToolCallBlock) for block in message.blocks
437
+ )
408
438
  content_txt = (
409
439
  None
410
440
  if content_txt == ""
@@ -412,6 +442,7 @@ def to_openai_message_dict(
412
442
  and (
413
443
  "function_call" in message.additional_kwargs
414
444
  or "tool_calls" in message.additional_kwargs
445
+ or already_has_tool_calls
415
446
  )
416
447
  else content_txt
417
448
  )
@@ -437,6 +468,13 @@ def to_openai_message_dict(
437
468
  else content
438
469
  ),
439
470
  }
471
+ if already_has_tool_calls:
472
+ existing_tool_calls = []
473
+ for c in content:
474
+ existing_tool_calls.extend(c.get("tool_calls", []))
475
+
476
+ if existing_tool_calls:
477
+ message_dict["tool_calls"] = existing_tool_calls
440
478
 
441
479
  # TODO: O1 models do not support system prompts
442
480
  if (
@@ -447,10 +485,14 @@ def to_openai_message_dict(
447
485
  if message_dict["role"] == "system":
448
486
  message_dict["role"] = "developer"
449
487
 
450
- # NOTE: openai messages have additional arguments:
451
- # - function messages have `name`
452
- # - assistant messages have optional `function_call`
453
- message_dict.update(message.additional_kwargs)
488
+ if (
489
+ "tool_calls" in message.additional_kwargs
490
+ or "function_call" in message.additional_kwargs
491
+ ) and not already_has_tool_calls:
492
+ message_dict.update(message.additional_kwargs)
493
+
494
+ if "tool_call_id" in message.additional_kwargs:
495
+ message_dict["tool_call_id"] = message.additional_kwargs["tool_call_id"]
454
496
 
455
497
  null_keys = [key for key, value in message_dict.items() if value is None]
456
498
  # if drop_none is True, remove keys with None values
@@ -469,6 +511,8 @@ def to_openai_responses_message_dict(
469
511
  """Convert a ChatMessage to an OpenAI message dict."""
470
512
  content = []
471
513
  content_txt = ""
514
+ tool_calls = []
515
+ reasoning = []
472
516
 
473
517
  for block in message.blocks:
474
518
  if isinstance(block, TextBlock):
@@ -512,13 +556,41 @@ def to_openai_responses_message_dict(
512
556
  }
513
557
  )
514
558
  elif isinstance(block, ThinkingBlock):
515
- if block.content:
516
- content.append({"type": "output_text", "text": block.content})
517
- content_txt += block.content
559
+ if block.content and "id" in block.additional_information:
560
+ reasoning.append(
561
+ {
562
+ "type": "reasoning",
563
+ "id": block.additional_information["id"],
564
+ "summary": [
565
+ {"type": "summary_text", "text": block.content or ""}
566
+ ],
567
+ }
568
+ )
569
+ elif isinstance(block, ToolCallBlock):
570
+ tool_calls.extend(
571
+ [
572
+ {
573
+ "type": "function_call",
574
+ "arguments": block.tool_kwargs,
575
+ "call_id": block.tool_call_id,
576
+ "name": block.tool_name,
577
+ }
578
+ ]
579
+ )
518
580
  else:
519
581
  msg = f"Unsupported content block type: {type(block).__name__}"
520
582
  raise ValueError(msg)
521
583
 
584
+ if "tool_calls" in message.additional_kwargs:
585
+ message_dicts = [
586
+ tool_call if isinstance(tool_call, dict) else tool_call.model_dump()
587
+ for tool_call in message.additional_kwargs["tool_calls"]
588
+ ]
589
+
590
+ return [*reasoning, *message_dicts]
591
+ elif tool_calls:
592
+ return [*reasoning, *tool_calls]
593
+
522
594
  # NOTE: Sending a null value (None) for Tool Message to OpenAI will cause error
523
595
  # It's only Allowed to send None if it's an Assistant Message and either a function call or tool calls were performed
524
596
  # Reference: https://platform.openai.com/docs/api-reference/chat/create
@@ -553,13 +625,6 @@ def to_openai_responses_message_dict(
553
625
  }
554
626
 
555
627
  return message_dict
556
- elif "tool_calls" in message.additional_kwargs:
557
- message_dicts = [
558
- tool_call if isinstance(tool_call, dict) else tool_call.model_dump()
559
- for tool_call in message.additional_kwargs["tool_calls"]
560
- ]
561
-
562
- return message_dicts
563
628
 
564
629
  # there are some cases (like image generation or MCP tool call) that only support the string input
565
630
  # this is why, if context_txt is a non-empty string, all the blocks are TextBlocks and the role is user, we return directly context_txt
@@ -596,6 +661,9 @@ def to_openai_responses_message_dict(
596
661
  for key in null_keys:
597
662
  message_dict.pop(key)
598
663
 
664
+ if reasoning:
665
+ return [*reasoning, message_dict]
666
+
599
667
  return message_dict # type: ignore
600
668
 
601
669
 
@@ -648,13 +716,22 @@ def from_openai_message(
648
716
  role = openai_message.role
649
717
  # NOTE: Azure OpenAI returns function calling messages without a content key
650
718
  if "text" in modalities and openai_message.content:
651
- blocks = [TextBlock(text=openai_message.content or "")]
719
+ blocks: List[ContentBlock] = [TextBlock(text=openai_message.content or "")]
652
720
  else:
653
- blocks = []
721
+ blocks: List[ContentBlock] = []
654
722
 
655
723
  additional_kwargs: Dict[str, Any] = {}
656
724
  if openai_message.tool_calls:
657
725
  tool_calls: List[ChatCompletionMessageToolCall] = openai_message.tool_calls
726
+ for tool_call in tool_calls:
727
+ if tool_call.function:
728
+ blocks.append(
729
+ ToolCallBlock(
730
+ tool_call_id=tool_call.id,
731
+ tool_name=tool_call.function.name or "",
732
+ tool_kwargs=tool_call.function.arguments or {},
733
+ )
734
+ )
658
735
  additional_kwargs.update(tool_calls=tool_calls)
659
736
 
660
737
  if openai_message.audio and "audio" in modalities:
@@ -742,6 +819,14 @@ def from_openai_message_dict(message_dict: dict) -> ChatMessage:
742
819
  blocks.append(ImageBlock(image=img, detail=detail))
743
820
  else:
744
821
  blocks.append(ImageBlock(url=img, detail=detail))
822
+ elif t == "function_call":
823
+ blocks.append(
824
+ ToolCallBlock(
825
+ tool_call_id=elem.get("call_id"),
826
+ tool_name=elem.get("name", ""),
827
+ tool_kwargs=elem.get("arguments", {}),
828
+ )
829
+ )
745
830
  else:
746
831
  msg = f"Unsupported message type: {t}"
747
832
  raise ValueError(msg)
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llama-index-llms-openai
3
- Version: 0.6.4
3
+ Version: 0.6.6
4
4
  Summary: llama-index llms openai integration
5
5
  Author: llama-index
6
6
  License-Expression: MIT
7
7
  License-File: LICENSE
8
8
  Requires-Python: <4.0,>=3.9
9
- Requires-Dist: llama-index-core<0.15,>=0.14.3
9
+ Requires-Dist: llama-index-core<0.15,>=0.14.5
10
10
  Requires-Dist: openai<2,>=1.108.1
11
11
  Description-Content-Type: text/markdown
12
12
 
@@ -0,0 +1,9 @@
1
+ llama_index/llms/openai/__init__.py,sha256=8nmgixeXifQ4eVSgtCic54WxXqrrpXQPL4rhACWCSFs,229
2
+ llama_index/llms/openai/base.py,sha256=AYVlujytA5HUndX0CojRN4Hdu2e_kq_cJ1LhXNiy-SA,44696
3
+ llama_index/llms/openai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ llama_index/llms/openai/responses.py,sha256=R7Y0GRptw5tXKQKiz9Mt-lrB6VLNG6HruMvZXiFUrYM,38012
5
+ llama_index/llms/openai/utils.py,sha256=V5iqwmx-15XCX57nAEiCX0DAvrbM76U4rTd4LisDpcg,33560
6
+ llama_index_llms_openai-0.6.6.dist-info/METADATA,sha256=JQXZjVvCOL_l25rDgIWA00qkrOAwOT8JkWUdRj24-eE,3039
7
+ llama_index_llms_openai-0.6.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
+ llama_index_llms_openai-0.6.6.dist-info/licenses/LICENSE,sha256=JPQLUZD9rKvCTdu192Nk0V5PAwklIg6jANii3UmTyMs,1065
9
+ llama_index_llms_openai-0.6.6.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- llama_index/llms/openai/__init__.py,sha256=8nmgixeXifQ4eVSgtCic54WxXqrrpXQPL4rhACWCSFs,229
2
- llama_index/llms/openai/base.py,sha256=tStcBE6oGKWF2rEyW375K7uehk8HF4q4Cv9t_9cVVqk,41908
3
- llama_index/llms/openai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- llama_index/llms/openai/responses.py,sha256=FjLPdjWE_Jb-Z3FbFKeKCig-5C4XO8Xz7LJKwFDHD7M,38131
5
- llama_index/llms/openai/utils.py,sha256=tBGihn2NgdMD8yB18JsWvrGSjrljbt6Pscvm63OhKg0,30196
6
- llama_index_llms_openai-0.6.4.dist-info/METADATA,sha256=TpC4abPx68QXEg9uHiP9AT5ltBHxxyzw5DFQUQu-6dI,3039
7
- llama_index_llms_openai-0.6.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
- llama_index_llms_openai-0.6.4.dist-info/licenses/LICENSE,sha256=JPQLUZD9rKvCTdu192Nk0V5PAwklIg6jANii3UmTyMs,1065
9
- llama_index_llms_openai-0.6.4.dist-info/RECORD,,