grasp_agents 0.2.11__py3-none-any.whl → 0.3.1__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.
Files changed (51) hide show
  1. grasp_agents/__init__.py +15 -14
  2. grasp_agents/cloud_llm.py +118 -131
  3. grasp_agents/comm_processor.py +201 -0
  4. grasp_agents/generics_utils.py +15 -7
  5. grasp_agents/llm.py +60 -31
  6. grasp_agents/llm_agent.py +229 -273
  7. grasp_agents/llm_agent_memory.py +58 -0
  8. grasp_agents/llm_policy_executor.py +482 -0
  9. grasp_agents/memory.py +20 -134
  10. grasp_agents/message_history.py +140 -0
  11. grasp_agents/openai/__init__.py +54 -36
  12. grasp_agents/openai/completion_chunk_converters.py +78 -0
  13. grasp_agents/openai/completion_converters.py +53 -30
  14. grasp_agents/openai/content_converters.py +13 -14
  15. grasp_agents/openai/converters.py +44 -68
  16. grasp_agents/openai/message_converters.py +58 -72
  17. grasp_agents/openai/openai_llm.py +101 -42
  18. grasp_agents/openai/tool_converters.py +24 -19
  19. grasp_agents/packet.py +24 -0
  20. grasp_agents/packet_pool.py +91 -0
  21. grasp_agents/printer.py +29 -15
  22. grasp_agents/processor.py +194 -0
  23. grasp_agents/prompt_builder.py +175 -192
  24. grasp_agents/run_context.py +20 -37
  25. grasp_agents/typing/completion.py +58 -12
  26. grasp_agents/typing/completion_chunk.py +173 -0
  27. grasp_agents/typing/converters.py +8 -12
  28. grasp_agents/typing/events.py +86 -0
  29. grasp_agents/typing/io.py +4 -13
  30. grasp_agents/typing/message.py +12 -50
  31. grasp_agents/typing/tool.py +52 -26
  32. grasp_agents/usage_tracker.py +6 -6
  33. grasp_agents/utils.py +3 -3
  34. grasp_agents/workflow/looped_workflow.py +132 -0
  35. grasp_agents/workflow/parallel_processor.py +95 -0
  36. grasp_agents/workflow/sequential_workflow.py +66 -0
  37. grasp_agents/workflow/workflow_processor.py +78 -0
  38. {grasp_agents-0.2.11.dist-info → grasp_agents-0.3.1.dist-info}/METADATA +41 -50
  39. grasp_agents-0.3.1.dist-info/RECORD +51 -0
  40. grasp_agents/agent_message.py +0 -27
  41. grasp_agents/agent_message_pool.py +0 -92
  42. grasp_agents/base_agent.py +0 -51
  43. grasp_agents/comm_agent.py +0 -217
  44. grasp_agents/llm_agent_state.py +0 -79
  45. grasp_agents/tool_orchestrator.py +0 -203
  46. grasp_agents/workflow/looped_agent.py +0 -134
  47. grasp_agents/workflow/sequential_agent.py +0 -72
  48. grasp_agents/workflow/workflow_agent.py +0 -88
  49. grasp_agents-0.2.11.dist-info/RECORD +0 -46
  50. {grasp_agents-0.2.11.dist-info → grasp_agents-0.3.1.dist-info}/WHEEL +0 -0
  51. {grasp_agents-0.2.11.dist-info → grasp_agents-0.3.1.dist-info}/licenses/LICENSE.md +0 -0
@@ -0,0 +1,140 @@
1
+ import logging
2
+ from collections.abc import Iterator, Sequence
3
+ from copy import deepcopy
4
+
5
+ from .typing.io import LLMPrompt
6
+ from .typing.message import Message, Messages, SystemMessage
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ class MessageHistory:
12
+ def __init__(self, sys_prompt: LLMPrompt | None = None) -> None:
13
+ self._sys_prompt = sys_prompt
14
+ self._conversations: list[Messages]
15
+ self.reset()
16
+
17
+ @property
18
+ def sys_prompt(self) -> LLMPrompt | None:
19
+ return self._sys_prompt
20
+
21
+ def add_message_batch(self, message_batch: Sequence[Message]) -> None:
22
+ """
23
+ Adds a batch of messages to the current batched conversations.
24
+ This method verifies that the size of the input message batch matches
25
+ the expected batch size (self.batch_size).
26
+ If there is a mismatch, the method adjusts by duplicating either
27
+ the message or the conversation as necessary:
28
+
29
+ - If the message batch contains exactly one message and
30
+ self.batch_size > 1, the single message is duplicated to match
31
+ the batch size.
32
+ - If the message batch contains multiple messages but
33
+ self.batch_size == 1, the entire conversation is duplicated to
34
+ accommodate each message in the batch.
35
+ - If the message batch size does not match self.batch_size and none of
36
+ the above adjustments apply, a ValueError is raised.
37
+
38
+ Afterwards, each message in the batch is appended to its corresponding
39
+ conversation in the batched conversations.
40
+
41
+ Args:
42
+ message_batch: A sequence of Message objects
43
+ representing the batch of messages to be added. Must align with
44
+ or be adjusted to match the current batch size.
45
+
46
+ Raises:
47
+ ValueError: If the message batch size does not match the current
48
+ batch size and cannot be automatically adjusted.
49
+
50
+ """
51
+ message_batch_size = len(message_batch)
52
+
53
+ if message_batch_size == 1 and self.batch_size > 1:
54
+ logger.info(
55
+ "Message batch size is 1, current batch size is "
56
+ f"{self.batch_size}: duplicating the message to match the "
57
+ "current batch size"
58
+ )
59
+ message_batch = self._duplicate_message_to_current_batch_size(message_batch)
60
+ message_batch_size = self.batch_size
61
+ elif message_batch_size > 1 and self.batch_size == 1:
62
+ logger.info(
63
+ f"Message batch size is {len(message_batch)}, current batch "
64
+ "size is 1: duplicating the conversation to match the message "
65
+ "batch size"
66
+ )
67
+ self._duplicate_conversation_to_target_batch_size(message_batch_size)
68
+ elif message_batch_size != self.batch_size:
69
+ raise ValueError(
70
+ f"Message batch size {message_batch_size} does not match "
71
+ f"current batch size {self.batch_size}"
72
+ )
73
+
74
+ for batch_id in range(message_batch_size):
75
+ self._conversations[batch_id].append(message_batch[batch_id])
76
+
77
+ def add_message_batches(self, message_batches: Sequence[Sequence[Message]]) -> None:
78
+ for message_batch in message_batches:
79
+ self.add_message_batch(message_batch)
80
+
81
+ def add_message(self, message: Message) -> None:
82
+ for conversation in self._conversations:
83
+ conversation.append(message)
84
+
85
+ def add_message_list(self, message_list: Sequence[Message]) -> None:
86
+ for message in message_list:
87
+ self.add_message(message)
88
+
89
+ def __len__(self) -> int:
90
+ return len(self._conversations[0])
91
+
92
+ def __repr__(self) -> str:
93
+ return f"{self.__class__.__name__}(len={len(self)}; bs={self.batch_size})"
94
+
95
+ def __getitem__(self, idx: int) -> tuple[Message, ...]:
96
+ return tuple(conversation[idx] for conversation in self._conversations)
97
+
98
+ def __iter__(self) -> Iterator[tuple[Message, ...]]:
99
+ for idx in range(len(self)):
100
+ yield tuple(conversation[idx] for conversation in self._conversations)
101
+
102
+ def _duplicate_message_to_current_batch_size(
103
+ self, message_batch: Sequence[Message]
104
+ ) -> Sequence[Message]:
105
+ assert len(message_batch) == 1, (
106
+ "Message batch size must be 1 to duplicate to current batch size"
107
+ )
108
+
109
+ return [deepcopy(message_batch[0]) for _ in range(self.batch_size)]
110
+
111
+ def _duplicate_conversation_to_target_batch_size(
112
+ self, target_batch_size: int
113
+ ) -> None:
114
+ assert self.batch_size == 1, "Batch size must be 1 to duplicate conversation"
115
+ self._conversations = [
116
+ deepcopy(self._conversations[0]) for _ in range(target_batch_size)
117
+ ]
118
+
119
+ @property
120
+ def conversations(self) -> list[Messages]:
121
+ return self._conversations
122
+
123
+ @property
124
+ def batch_size(self) -> int:
125
+ return len(self._conversations)
126
+
127
+ def reset(
128
+ self, sys_prompt: LLMPrompt | None = None, *, batch_size: int = 1
129
+ ) -> None:
130
+ if sys_prompt is not None:
131
+ self._sys_prompt = sys_prompt
132
+
133
+ conv: Messages = []
134
+ if self._sys_prompt is not None:
135
+ conv.append(SystemMessage(content=self._sys_prompt))
136
+
137
+ self._conversations = [deepcopy(conv) for _ in range(batch_size)]
138
+
139
+ def erase(self) -> None:
140
+ self._conversations = [[]]
@@ -1,87 +1,105 @@
1
1
  # pyright: reportUnusedImport=false
2
2
 
3
3
  from openai._streaming import (
4
- AsyncStream as ChatCompletionAsyncStream, # type: ignore[import] # noqa: PLC2701
4
+ AsyncStream as OpenAIAsyncStream, # type: ignore[import] # noqa: PLC2701
5
+ )
6
+ from openai.types import CompletionUsage as OpenAICompletionUsage
7
+ from openai.types.chat.chat_completion import ChatCompletion as OpenAICompletion
8
+ from openai.types.chat.chat_completion import (
9
+ ChoiceLogprobs as OpenAIChoiceLogprobs,
5
10
  )
6
- from openai.types import CompletionUsage as ChatCompletionUsage
7
- from openai.types.chat.chat_completion import ChatCompletion
8
11
  from openai.types.chat.chat_completion_assistant_message_param import (
9
- ChatCompletionAssistantMessageParam,
12
+ ChatCompletionAssistantMessageParam as OpenAIAssistantMessageParam,
13
+ )
14
+ from openai.types.chat.chat_completion_chunk import (
15
+ ChatCompletionChunk as OpenAICompletionChunk,
16
+ )
17
+ from openai.types.chat.chat_completion_chunk import (
18
+ Choice as CompletionChunkChoice,
19
+ )
20
+ from openai.types.chat.chat_completion_chunk import (
21
+ Choice as OpenAIChunkChoice,
22
+ )
23
+ from openai.types.chat.chat_completion_chunk import (
24
+ ChoiceDelta as CompletionChunkChoiceDelta,
25
+ )
26
+ from openai.types.chat.chat_completion_chunk import (
27
+ ChoiceDelta as OpenAIChunkChoiceDelta,
28
+ )
29
+ from openai.types.chat.chat_completion_chunk import (
30
+ ChoiceDeltaToolCall as OpenAIChunkChoiceDeltaToolCall,
10
31
  )
11
- from openai.types.chat.chat_completion_chunk import ChatCompletionChunk
12
32
  from openai.types.chat.chat_completion_content_part_image_param import (
13
- ChatCompletionContentPartImageParam,
33
+ ChatCompletionContentPartImageParam as OpenAIContentPartImageParam,
14
34
  )
15
35
  from openai.types.chat.chat_completion_content_part_image_param import (
16
- ImageURL as ChatCompletionImageURL,
36
+ ImageURL as OpenAIImageURL,
17
37
  )
18
38
  from openai.types.chat.chat_completion_content_part_param import (
19
- ChatCompletionContentPartParam,
39
+ ChatCompletionContentPartParam as OpenAIContentPartParam,
20
40
  )
21
41
  from openai.types.chat.chat_completion_content_part_text_param import (
22
- ChatCompletionContentPartTextParam,
42
+ ChatCompletionContentPartTextParam as OpenAIContentPartTextParam,
23
43
  )
24
44
  from openai.types.chat.chat_completion_developer_message_param import (
25
- ChatCompletionDeveloperMessageParam,
45
+ ChatCompletionDeveloperMessageParam as OpenAIDeveloperMessageParam,
26
46
  )
27
47
  from openai.types.chat.chat_completion_function_message_param import (
28
- ChatCompletionFunctionMessageParam,
48
+ ChatCompletionFunctionMessageParam as OpenAIFunctionMessageParam,
49
+ )
50
+ from openai.types.chat.chat_completion_message import (
51
+ ChatCompletionMessage as OpenAIMessage,
29
52
  )
30
- from openai.types.chat.chat_completion_message import ChatCompletionMessage
31
53
  from openai.types.chat.chat_completion_message_param import (
32
- ChatCompletionMessageParam,
54
+ ChatCompletionMessageParam as OpenAIMessageParam,
33
55
  )
34
56
  from openai.types.chat.chat_completion_message_tool_call_param import (
35
- ChatCompletionMessageToolCallParam,
57
+ ChatCompletionMessageToolCallParam as OpenAIToolCallParam,
36
58
  )
37
59
  from openai.types.chat.chat_completion_message_tool_call_param import (
38
- Function as ChatCompletionToolCallFunction,
60
+ Function as OpenAIToolCallFunction,
39
61
  )
40
62
  from openai.types.chat.chat_completion_named_tool_choice_param import (
41
- ChatCompletionNamedToolChoiceParam,
63
+ ChatCompletionNamedToolChoiceParam as OpenAINamedToolChoiceParam,
42
64
  )
43
65
  from openai.types.chat.chat_completion_named_tool_choice_param import (
44
- Function as ChatCompletionNamedToolChoiceFunction,
66
+ Function as OpenAINamedToolChoiceFunction,
45
67
  )
46
68
  from openai.types.chat.chat_completion_prediction_content_param import (
47
- ChatCompletionPredictionContentParam,
69
+ ChatCompletionPredictionContentParam as OpenAIPredictionContentParam,
48
70
  )
49
71
  from openai.types.chat.chat_completion_stream_options_param import (
50
- ChatCompletionStreamOptionsParam,
72
+ ChatCompletionStreamOptionsParam as OpenAIStreamOptionsParam,
51
73
  )
52
74
  from openai.types.chat.chat_completion_system_message_param import (
53
- ChatCompletionSystemMessageParam,
75
+ ChatCompletionSystemMessageParam as OpenAISystemMessageParam,
54
76
  )
55
77
  from openai.types.chat.chat_completion_tool_choice_option_param import (
56
- ChatCompletionToolChoiceOptionParam,
78
+ ChatCompletionToolChoiceOptionParam as OpenAIToolChoiceOptionParam,
57
79
  )
58
80
  from openai.types.chat.chat_completion_tool_message_param import (
59
- ChatCompletionToolMessageParam,
81
+ ChatCompletionToolMessageParam as OpenAIToolMessageParam,
60
82
  )
61
83
  from openai.types.chat.chat_completion_tool_param import (
62
- ChatCompletionToolParam,
84
+ ChatCompletionToolParam as OpenAIToolParam,
63
85
  )
64
86
  from openai.types.chat.chat_completion_user_message_param import (
65
- ChatCompletionUserMessageParam,
87
+ ChatCompletionUserMessageParam as OpenAIUserMessageParam,
66
88
  )
67
89
  from openai.types.chat.parsed_chat_completion import (
68
- ParsedChatCompletion,
69
- ParsedChatCompletionMessage,
70
- ParsedChoice,
71
- )
72
- from openai.types.shared_params.function_definition import (
73
- FunctionDefinition as ChatCompletionFunctionDefinition,
90
+ ParsedChatCompletion as OpenAIParsedCompletion,
74
91
  )
75
- from openai.types.shared_params.response_format_json_object import (
76
- ResponseFormatJSONObject,
92
+ from openai.types.chat.parsed_chat_completion import (
93
+ ParsedChatCompletionMessage as OpenAIParsedMessage,
77
94
  )
78
- from openai.types.shared_params.response_format_json_schema import (
79
- ResponseFormatJSONSchema,
95
+ from openai.types.chat.parsed_chat_completion import (
96
+ ParsedChoice as OpenAIParsedChoice,
80
97
  )
81
- from openai.types.shared_params.response_format_text import (
82
- ResponseFormatText,
98
+ from openai.types.shared_params.function_definition import (
99
+ FunctionDefinition as OpenAIFunctionDefinition,
83
100
  )
84
101
 
102
+ # from openai.beta.types.chat.
85
103
  from .openai_llm import OpenAILLM, OpenAILLMSettings
86
104
 
87
105
  __all__ = ["OpenAILLM", "OpenAILLMSettings"]
@@ -0,0 +1,78 @@
1
+ from ..typing.completion_chunk import (
2
+ CompletionChunk,
3
+ CompletionChunkChoice,
4
+ CompletionChunkChoiceDelta,
5
+ CompletionChunkDeltaToolCall,
6
+ )
7
+ from . import OpenAICompletionChunk # type: ignore[import]
8
+ from .completion_converters import from_api_completion_usage
9
+
10
+
11
+ def from_api_completion_chunk(
12
+ api_completion_chunk: OpenAICompletionChunk, name: str | None = None
13
+ ) -> CompletionChunk:
14
+ if api_completion_chunk.choices is None: # type: ignore
15
+ raise RuntimeError(
16
+ f"Completion chunk API error: "
17
+ f"{getattr(api_completion_chunk, 'error', None)}"
18
+ )
19
+
20
+ choices: list[CompletionChunkChoice] = []
21
+
22
+ for api_choice in api_completion_chunk.choices:
23
+ index = api_choice.index
24
+ finish_reason = api_choice.finish_reason
25
+
26
+ if api_choice.delta is None: # type: ignore
27
+ raise RuntimeError(
28
+ "API returned None for delta content in completion chunk "
29
+ f"with finish_reason: {finish_reason}."
30
+ )
31
+ # if api_choice.delta.content is None:
32
+ # raise RuntimeError(
33
+ # "API returned None for delta content in completion chunk "
34
+ # f"with finish_reason: {finish_reason}."
35
+ # )
36
+
37
+ delta = CompletionChunkChoiceDelta(
38
+ content=api_choice.delta.content,
39
+ refusal=api_choice.delta.refusal,
40
+ role=api_choice.delta.role,
41
+ tool_calls=[
42
+ CompletionChunkDeltaToolCall(
43
+ id=tool_call.id,
44
+ index=tool_call.index,
45
+ tool_name=tool_call.function.name,
46
+ tool_arguments=tool_call.function.arguments,
47
+ )
48
+ for tool_call in (api_choice.delta.tool_calls or [])
49
+ if tool_call.function
50
+ ],
51
+ )
52
+
53
+ choice = CompletionChunkChoice(
54
+ index=index,
55
+ delta=delta,
56
+ finish_reason=finish_reason,
57
+ # OpenAI logprobs have identical, but separately defined, types for
58
+ # completion chunks and completions
59
+ logprobs=api_choice.logprobs,
60
+ )
61
+
62
+ choices.append(choice)
63
+
64
+ usage = (
65
+ from_api_completion_usage(api_completion_chunk.usage)
66
+ if api_completion_chunk.usage
67
+ else None
68
+ )
69
+
70
+ return CompletionChunk(
71
+ id=api_completion_chunk.id,
72
+ model=api_completion_chunk.model,
73
+ name=name,
74
+ created=api_completion_chunk.created,
75
+ system_fingerprint=api_completion_chunk.system_fingerprint,
76
+ choices=choices,
77
+ usage=usage,
78
+ )
@@ -1,18 +1,34 @@
1
- from collections.abc import AsyncIterator
2
-
3
- from ..typing.completion import Completion, CompletionChoice, CompletionChunk
4
- from . import (
5
- ChatCompletion,
6
- ChatCompletionAsyncStream, # type: ignore[import]
7
- ChatCompletionChunk,
8
- )
1
+ from ..typing.completion import Completion, CompletionChoice, Usage
2
+ from . import OpenAICompletion, OpenAICompletionUsage
9
3
  from .message_converters import from_api_assistant_message
10
4
 
11
5
 
6
+ def from_api_completion_usage(api_usage: OpenAICompletionUsage) -> Usage:
7
+ reasoning_tokens = None
8
+ cached_tokens = None
9
+
10
+ if api_usage.completion_tokens_details is not None:
11
+ reasoning_tokens = api_usage.completion_tokens_details.reasoning_tokens
12
+ if api_usage.prompt_tokens_details is not None:
13
+ cached_tokens = api_usage.prompt_tokens_details.cached_tokens
14
+
15
+ input_tokens = api_usage.prompt_tokens - (cached_tokens or 0)
16
+ output_tokens = api_usage.completion_tokens - (reasoning_tokens or 0)
17
+
18
+ return Usage(
19
+ input_tokens=input_tokens,
20
+ output_tokens=output_tokens,
21
+ reasoning_tokens=reasoning_tokens,
22
+ cached_tokens=cached_tokens,
23
+ )
24
+
25
+
12
26
  def from_api_completion(
13
- api_completion: ChatCompletion, model_id: str | None = None
27
+ api_completion: OpenAICompletion, name: str | None = None
14
28
  ) -> Completion:
15
29
  choices: list[CompletionChoice] = []
30
+ usage: Usage | None = None
31
+
16
32
  if api_completion.choices is None: # type: ignore
17
33
  # Some providers return None for the choices when there is an error
18
34
  # TODO: add custom error types
@@ -20,36 +36,43 @@ def from_api_completion(
20
36
  f"Completion API error: {getattr(api_completion, 'error', None)}"
21
37
  )
22
38
  for api_choice in api_completion.choices:
23
- # TODO: currently no way to assign individual message usages when len(choices) > 1
39
+ index = api_choice.index
24
40
  finish_reason = api_choice.finish_reason
41
+
25
42
  # Some providers return None for the message when finish_reason is other than "stop"
26
43
  if api_choice.message is None: # type: ignore
27
44
  raise RuntimeError(
28
45
  f"API returned None for message with finish_reason: {finish_reason}"
29
46
  )
30
- message = from_api_assistant_message(
31
- api_choice.message, api_completion.usage, model_id=model_id
32
- )
33
- choices.append(CompletionChoice(message=message, finish_reason=finish_reason))
34
47
 
35
- return Completion(choices=choices, model_id=model_id)
36
-
37
-
38
- def to_api_completion(completion: Completion) -> ChatCompletion:
39
- raise NotImplementedError
48
+ # usage = from_api_completion_usage(api_usage) if api_usage else None
40
49
 
50
+ usage = (
51
+ from_api_completion_usage(api_completion.usage)
52
+ if api_completion.usage
53
+ else None
54
+ )
55
+ message = from_api_assistant_message(api_choice.message, name=name)
41
56
 
42
- def from_api_completion_chunk(
43
- api_completion_chunk: ChatCompletionChunk, model_id: str | None = None
44
- ) -> CompletionChunk:
45
- delta = api_completion_chunk.choices[0].delta.content
57
+ choices.append(
58
+ CompletionChoice(
59
+ index=index,
60
+ message=message,
61
+ finish_reason=finish_reason,
62
+ logprobs=api_choice.logprobs,
63
+ )
64
+ )
46
65
 
47
- return CompletionChunk(delta=delta, model_id=model_id)
66
+ return Completion(
67
+ id=api_completion.id,
68
+ created=api_completion.created,
69
+ system_fingerprint=api_completion.system_fingerprint,
70
+ usage=usage,
71
+ choices=choices,
72
+ model=api_completion.model,
73
+ name=name,
74
+ )
48
75
 
49
76
 
50
- async def from_api_completion_chunk_iterator(
51
- api_completion_chunk_iterator: ChatCompletionAsyncStream[ChatCompletionChunk],
52
- model_id: str | None = None,
53
- ) -> AsyncIterator[CompletionChunk]:
54
- async for api_chunk in api_completion_chunk_iterator:
55
- yield from_api_completion_chunk(api_chunk, model_id=model_id)
77
+ def to_api_completion(completion: Completion) -> OpenAICompletion:
78
+ raise NotImplementedError
@@ -8,10 +8,10 @@ from ..typing.content import (
8
8
  ImageData,
9
9
  )
10
10
  from . import (
11
- ChatCompletionContentPartImageParam,
12
- ChatCompletionContentPartParam,
13
- ChatCompletionContentPartTextParam,
14
- ChatCompletionImageURL,
11
+ OpenAIContentPartImageParam,
12
+ OpenAIContentPartParam,
13
+ OpenAIContentPartTextParam,
14
+ OpenAIImageURL,
15
15
  )
16
16
 
17
17
  BASE64_PREFIX = "data:image/jpeg;base64,"
@@ -26,7 +26,7 @@ def image_data_to_str(image_data: ImageData) -> str:
26
26
 
27
27
 
28
28
  def from_api_content(
29
- api_content: str | Iterable[ChatCompletionContentPartParam],
29
+ api_content: str | Iterable[OpenAIContentPartParam],
30
30
  ) -> "Content":
31
31
  if isinstance(api_content, str):
32
32
  return Content(parts=[ContentPartText(data=api_content)])
@@ -48,7 +48,7 @@ def from_api_content(
48
48
  detail=detail,
49
49
  )
50
50
  else:
51
- image_data = ImageData.from_url(img_url=url, detail=detail) # type: ignore
51
+ image_data = ImageData.from_url(img_url=url, detail=detail)
52
52
  content_part = ContentPartImage(data=image_data)
53
53
 
54
54
  content_parts.append(content_part) # type: ignore
@@ -58,19 +58,18 @@ def from_api_content(
58
58
 
59
59
  def to_api_content(
60
60
  content: Content,
61
- ) -> Iterable[ChatCompletionContentPartParam]:
62
- api_content: list[ChatCompletionContentPartParam] = []
61
+ ) -> Iterable[OpenAIContentPartParam]:
62
+ api_content: list[OpenAIContentPartParam] = []
63
63
  for content_part in content.parts:
64
- api_content_part: ChatCompletionContentPartParam
64
+ api_content_part: OpenAIContentPartParam
65
65
  if isinstance(content_part, ContentPartText):
66
- api_content_part = ChatCompletionContentPartTextParam(
67
- type="text",
68
- text=content_part.data,
66
+ api_content_part = OpenAIContentPartTextParam(
67
+ type="text", text=content_part.data
69
68
  )
70
69
  else:
71
- api_content_part = ChatCompletionContentPartImageParam(
70
+ api_content_part = OpenAIContentPartImageParam(
72
71
  type="image_url",
73
- image_url=ChatCompletionImageURL(
72
+ image_url=OpenAIImageURL(
74
73
  url=image_data_to_str(content_part.data),
75
74
  detail=content_part.data.detail,
76
75
  ),