lfx-nightly 0.2.0.dev0__py3-none-any.whl → 0.2.0.dev41__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 (196) hide show
  1. lfx/_assets/component_index.json +1 -1
  2. lfx/base/agents/agent.py +21 -4
  3. lfx/base/agents/altk_base_agent.py +393 -0
  4. lfx/base/agents/altk_tool_wrappers.py +565 -0
  5. lfx/base/agents/events.py +2 -1
  6. lfx/base/composio/composio_base.py +159 -224
  7. lfx/base/data/base_file.py +97 -20
  8. lfx/base/data/docling_utils.py +61 -10
  9. lfx/base/data/storage_utils.py +301 -0
  10. lfx/base/data/utils.py +178 -14
  11. lfx/base/mcp/util.py +2 -2
  12. lfx/base/models/anthropic_constants.py +21 -12
  13. lfx/base/models/groq_constants.py +74 -58
  14. lfx/base/models/groq_model_discovery.py +265 -0
  15. lfx/base/models/model.py +1 -1
  16. lfx/base/models/model_utils.py +100 -0
  17. lfx/base/models/openai_constants.py +7 -0
  18. lfx/base/models/watsonx_constants.py +32 -8
  19. lfx/base/tools/run_flow.py +601 -129
  20. lfx/cli/commands.py +9 -4
  21. lfx/cli/common.py +2 -2
  22. lfx/cli/run.py +1 -1
  23. lfx/cli/script_loader.py +53 -11
  24. lfx/components/Notion/create_page.py +1 -1
  25. lfx/components/Notion/list_database_properties.py +1 -1
  26. lfx/components/Notion/list_pages.py +1 -1
  27. lfx/components/Notion/list_users.py +1 -1
  28. lfx/components/Notion/page_content_viewer.py +1 -1
  29. lfx/components/Notion/search.py +1 -1
  30. lfx/components/Notion/update_page_property.py +1 -1
  31. lfx/components/__init__.py +19 -5
  32. lfx/components/{agents → altk}/__init__.py +5 -9
  33. lfx/components/altk/altk_agent.py +193 -0
  34. lfx/components/apify/apify_actor.py +1 -1
  35. lfx/components/composio/__init__.py +70 -18
  36. lfx/components/composio/apollo_composio.py +11 -0
  37. lfx/components/composio/bitbucket_composio.py +11 -0
  38. lfx/components/composio/canva_composio.py +11 -0
  39. lfx/components/composio/coda_composio.py +11 -0
  40. lfx/components/composio/composio_api.py +10 -0
  41. lfx/components/composio/discord_composio.py +1 -1
  42. lfx/components/composio/elevenlabs_composio.py +11 -0
  43. lfx/components/composio/exa_composio.py +11 -0
  44. lfx/components/composio/firecrawl_composio.py +11 -0
  45. lfx/components/composio/fireflies_composio.py +11 -0
  46. lfx/components/composio/gmail_composio.py +1 -1
  47. lfx/components/composio/googlebigquery_composio.py +11 -0
  48. lfx/components/composio/googlecalendar_composio.py +1 -1
  49. lfx/components/composio/googledocs_composio.py +1 -1
  50. lfx/components/composio/googlemeet_composio.py +1 -1
  51. lfx/components/composio/googlesheets_composio.py +1 -1
  52. lfx/components/composio/googletasks_composio.py +1 -1
  53. lfx/components/composio/heygen_composio.py +11 -0
  54. lfx/components/composio/mem0_composio.py +11 -0
  55. lfx/components/composio/peopledatalabs_composio.py +11 -0
  56. lfx/components/composio/perplexityai_composio.py +11 -0
  57. lfx/components/composio/serpapi_composio.py +11 -0
  58. lfx/components/composio/slack_composio.py +3 -574
  59. lfx/components/composio/slackbot_composio.py +1 -1
  60. lfx/components/composio/snowflake_composio.py +11 -0
  61. lfx/components/composio/tavily_composio.py +11 -0
  62. lfx/components/composio/youtube_composio.py +2 -2
  63. lfx/components/cuga/__init__.py +34 -0
  64. lfx/components/cuga/cuga_agent.py +730 -0
  65. lfx/components/data/__init__.py +78 -28
  66. lfx/components/data_source/__init__.py +58 -0
  67. lfx/components/{data → data_source}/api_request.py +26 -3
  68. lfx/components/{data → data_source}/csv_to_data.py +15 -10
  69. lfx/components/{data → data_source}/json_to_data.py +15 -8
  70. lfx/components/{data → data_source}/news_search.py +1 -1
  71. lfx/components/{data → data_source}/rss.py +1 -1
  72. lfx/components/{data → data_source}/sql_executor.py +1 -1
  73. lfx/components/{data → data_source}/url.py +1 -1
  74. lfx/components/{data → data_source}/web_search.py +1 -1
  75. lfx/components/datastax/astradb_cql.py +1 -1
  76. lfx/components/datastax/astradb_graph.py +1 -1
  77. lfx/components/datastax/astradb_tool.py +1 -1
  78. lfx/components/datastax/astradb_vectorstore.py +1 -1
  79. lfx/components/datastax/hcd.py +1 -1
  80. lfx/components/deactivated/json_document_builder.py +1 -1
  81. lfx/components/docling/__init__.py +0 -3
  82. lfx/components/docling/chunk_docling_document.py +3 -1
  83. lfx/components/docling/export_docling_document.py +3 -1
  84. lfx/components/elastic/elasticsearch.py +1 -1
  85. lfx/components/files_and_knowledge/__init__.py +47 -0
  86. lfx/components/{data → files_and_knowledge}/directory.py +1 -1
  87. lfx/components/{data → files_and_knowledge}/file.py +304 -24
  88. lfx/components/{knowledge_bases → files_and_knowledge}/retrieval.py +2 -2
  89. lfx/components/{data → files_and_knowledge}/save_file.py +218 -31
  90. lfx/components/flow_controls/__init__.py +58 -0
  91. lfx/components/{logic → flow_controls}/conditional_router.py +1 -1
  92. lfx/components/{logic → flow_controls}/loop.py +43 -9
  93. lfx/components/flow_controls/run_flow.py +108 -0
  94. lfx/components/glean/glean_search_api.py +1 -1
  95. lfx/components/groq/groq.py +35 -28
  96. lfx/components/helpers/__init__.py +102 -0
  97. lfx/components/ibm/watsonx.py +7 -1
  98. lfx/components/input_output/__init__.py +3 -1
  99. lfx/components/input_output/chat.py +4 -3
  100. lfx/components/input_output/chat_output.py +10 -4
  101. lfx/components/input_output/text.py +1 -1
  102. lfx/components/input_output/text_output.py +1 -1
  103. lfx/components/{data → input_output}/webhook.py +1 -1
  104. lfx/components/knowledge_bases/__init__.py +59 -4
  105. lfx/components/langchain_utilities/character.py +1 -1
  106. lfx/components/langchain_utilities/csv_agent.py +84 -16
  107. lfx/components/langchain_utilities/json_agent.py +67 -12
  108. lfx/components/langchain_utilities/language_recursive.py +1 -1
  109. lfx/components/llm_operations/__init__.py +46 -0
  110. lfx/components/{processing → llm_operations}/batch_run.py +17 -8
  111. lfx/components/{processing → llm_operations}/lambda_filter.py +1 -1
  112. lfx/components/{logic → llm_operations}/llm_conditional_router.py +1 -1
  113. lfx/components/{processing/llm_router.py → llm_operations/llm_selector.py} +3 -3
  114. lfx/components/{processing → llm_operations}/structured_output.py +1 -1
  115. lfx/components/logic/__init__.py +126 -0
  116. lfx/components/mem0/mem0_chat_memory.py +11 -0
  117. lfx/components/models/__init__.py +64 -9
  118. lfx/components/models_and_agents/__init__.py +49 -0
  119. lfx/components/{agents → models_and_agents}/agent.py +6 -4
  120. lfx/components/models_and_agents/embedding_model.py +353 -0
  121. lfx/components/models_and_agents/language_model.py +398 -0
  122. lfx/components/{agents → models_and_agents}/mcp_component.py +53 -44
  123. lfx/components/{helpers → models_and_agents}/memory.py +1 -1
  124. lfx/components/nvidia/system_assist.py +1 -1
  125. lfx/components/olivya/olivya.py +1 -1
  126. lfx/components/ollama/ollama.py +24 -5
  127. lfx/components/processing/__init__.py +9 -60
  128. lfx/components/processing/converter.py +1 -1
  129. lfx/components/processing/dataframe_operations.py +1 -1
  130. lfx/components/processing/parse_json_data.py +2 -2
  131. lfx/components/processing/parser.py +1 -1
  132. lfx/components/processing/split_text.py +1 -1
  133. lfx/components/qdrant/qdrant.py +1 -1
  134. lfx/components/redis/redis.py +1 -1
  135. lfx/components/twelvelabs/split_video.py +10 -0
  136. lfx/components/twelvelabs/video_file.py +12 -0
  137. lfx/components/utilities/__init__.py +43 -0
  138. lfx/components/{helpers → utilities}/calculator_core.py +1 -1
  139. lfx/components/{helpers → utilities}/current_date.py +1 -1
  140. lfx/components/{processing → utilities}/python_repl_core.py +1 -1
  141. lfx/components/vectorstores/local_db.py +9 -0
  142. lfx/components/youtube/youtube_transcripts.py +118 -30
  143. lfx/custom/custom_component/component.py +57 -1
  144. lfx/custom/custom_component/custom_component.py +68 -6
  145. lfx/custom/directory_reader/directory_reader.py +5 -2
  146. lfx/graph/edge/base.py +43 -20
  147. lfx/graph/state/model.py +15 -2
  148. lfx/graph/utils.py +6 -0
  149. lfx/graph/vertex/param_handler.py +10 -7
  150. lfx/helpers/__init__.py +12 -0
  151. lfx/helpers/flow.py +117 -0
  152. lfx/inputs/input_mixin.py +24 -1
  153. lfx/inputs/inputs.py +13 -1
  154. lfx/interface/components.py +161 -83
  155. lfx/log/logger.py +5 -3
  156. lfx/schema/image.py +2 -12
  157. lfx/services/database/__init__.py +5 -0
  158. lfx/services/database/service.py +25 -0
  159. lfx/services/deps.py +87 -22
  160. lfx/services/interfaces.py +5 -0
  161. lfx/services/manager.py +24 -10
  162. lfx/services/mcp_composer/service.py +1029 -162
  163. lfx/services/session.py +5 -0
  164. lfx/services/settings/auth.py +18 -11
  165. lfx/services/settings/base.py +56 -30
  166. lfx/services/settings/constants.py +8 -0
  167. lfx/services/storage/local.py +108 -46
  168. lfx/services/storage/service.py +171 -29
  169. lfx/template/field/base.py +3 -0
  170. lfx/utils/image.py +29 -11
  171. lfx/utils/ssrf_protection.py +384 -0
  172. lfx/utils/validate_cloud.py +26 -0
  173. {lfx_nightly-0.2.0.dev0.dist-info → lfx_nightly-0.2.0.dev41.dist-info}/METADATA +38 -22
  174. {lfx_nightly-0.2.0.dev0.dist-info → lfx_nightly-0.2.0.dev41.dist-info}/RECORD +189 -160
  175. {lfx_nightly-0.2.0.dev0.dist-info → lfx_nightly-0.2.0.dev41.dist-info}/WHEEL +1 -1
  176. lfx/components/agents/altk_agent.py +0 -366
  177. lfx/components/agents/cuga_agent.py +0 -1013
  178. lfx/components/docling/docling_remote_vlm.py +0 -284
  179. lfx/components/logic/run_flow.py +0 -71
  180. lfx/components/models/embedding_model.py +0 -195
  181. lfx/components/models/language_model.py +0 -144
  182. lfx/components/processing/dataframe_to_toolset.py +0 -259
  183. /lfx/components/{data → data_source}/mock_data.py +0 -0
  184. /lfx/components/{knowledge_bases → files_and_knowledge}/ingestion.py +0 -0
  185. /lfx/components/{logic → flow_controls}/data_conditional_router.py +0 -0
  186. /lfx/components/{logic → flow_controls}/flow_tool.py +0 -0
  187. /lfx/components/{logic → flow_controls}/listen.py +0 -0
  188. /lfx/components/{logic → flow_controls}/notify.py +0 -0
  189. /lfx/components/{logic → flow_controls}/pass_message.py +0 -0
  190. /lfx/components/{logic → flow_controls}/sub_flow.py +0 -0
  191. /lfx/components/{processing → models_and_agents}/prompt.py +0 -0
  192. /lfx/components/{helpers → processing}/create_list.py +0 -0
  193. /lfx/components/{helpers → processing}/output_parser.py +0 -0
  194. /lfx/components/{helpers → processing}/store_message.py +0 -0
  195. /lfx/components/{helpers → utilities}/id_generator.py +0 -0
  196. {lfx_nightly-0.2.0.dev0.dist-info → lfx_nightly-0.2.0.dev41.dist-info}/entry_points.txt +0 -0
@@ -1,54 +1,196 @@
1
- """Base storage service for lfx package."""
1
+ from __future__ import annotations
2
2
 
3
- from abc import ABC, abstractmethod
4
- from pathlib import Path
3
+ from abc import abstractmethod
4
+ from typing import TYPE_CHECKING
5
5
 
6
+ import anyio
6
7
 
7
- class StorageService(ABC):
8
- """Abstract base class for storage services."""
8
+ from lfx.services.base import Service
9
9
 
10
- def __init__(self, data_dir: str | Path | None = None) -> None:
10
+ if TYPE_CHECKING:
11
+ from collections.abc import AsyncIterator
12
+
13
+ from lfx.services.settings.service import SettingsService
14
+
15
+
16
+ class StorageService(Service):
17
+ """Abstract base class for file storage services.
18
+
19
+ This class defines the interface for file storage operations that can be
20
+ implemented by different backends (local filesystem, S3, etc.).
21
+
22
+ All file operations are namespaced by flow_id to isolate files between
23
+ different flows or users.
24
+ """
25
+
26
+ name = "storage_service"
27
+
28
+ def __init__(self, session_service, settings_service: SettingsService):
11
29
  """Initialize the storage service.
12
30
 
13
31
  Args:
14
- data_dir: Directory path for storing data. Defaults to ~/.lfx/data
32
+ session_service: The session service instance
33
+ settings_service: The settings service instance containing configuration
34
+ """
35
+ self.settings_service = settings_service
36
+ self.session_service = session_service
37
+ self.data_dir: anyio.Path = anyio.Path(settings_service.settings.config_dir)
38
+ self.set_ready()
39
+
40
+ @abstractmethod
41
+ def build_full_path(self, flow_id: str, file_name: str) -> str:
42
+ """Build the full path/key for a file.
43
+
44
+ Args:
45
+ flow_id: The flow/user identifier for namespacing
46
+ file_name: The name of the file
47
+
48
+ Returns:
49
+ str: The full path or key for the file
15
50
  """
16
- if data_dir is None:
17
- data_dir = Path.home() / ".lfx" / "data"
18
- self.data_dir = Path(data_dir)
19
- self._ready = False
51
+ raise NotImplementedError
52
+
53
+ @abstractmethod
54
+ def parse_file_path(self, full_path: str) -> tuple[str, str]:
55
+ """Parse a full storage path to extract flow_id and file_name.
56
+
57
+ This reverses the build_full_path operation.
58
+
59
+ Args:
60
+ full_path: Full path as returned by build_full_path
61
+
62
+ Returns:
63
+ tuple[str, str]: A tuple of (flow_id, file_name)
64
+
65
+ Raises:
66
+ ValueError: If the path format is invalid or doesn't match expected structure
67
+ """
68
+ raise NotImplementedError
69
+
70
+ @abstractmethod
71
+ def resolve_component_path(self, logical_path: str) -> str:
72
+ """Convert a logical path to a format that components can use directly.
73
+
74
+ Logical paths are in the format "{flow_id}/{filename}" as stored in the database.
75
+ This method converts them to a format appropriate for the storage backend:
76
+ - Local storage: Absolute filesystem path (/data_dir/flow_id/filename)
77
+ - S3 storage: Logical path as-is (flow_id/filename)
78
+
79
+ Components receive this resolved path and can use it without knowing the
80
+ storage implementation details.
81
+
82
+ Args:
83
+ logical_path: Path in the format "flow_id/filename"
84
+
85
+ Returns:
86
+ str: A path that components can use directly
87
+ """
88
+ raise NotImplementedError
20
89
 
21
90
  def set_ready(self) -> None:
22
91
  """Mark the service as ready."""
23
92
  self._ready = True
24
- # Ensure the data directory exists
25
- self.data_dir.mkdir(parents=True, exist_ok=True)
26
-
27
- @property
28
- def ready(self) -> bool:
29
- """Check if the service is ready."""
30
- return self._ready
31
93
 
32
94
  @abstractmethod
33
- def build_full_path(self, flow_id: str, file_name: str) -> str:
34
- """Build the full path for a file."""
95
+ async def save_file(self, flow_id: str, file_name: str, data: bytes, *, append: bool = False) -> None:
96
+ """Save a file to storage.
35
97
 
36
- @abstractmethod
37
- async def save_file(self, flow_id: str, file_name: str, data: bytes) -> None:
38
- """Save a file."""
98
+ Args:
99
+ flow_id: The flow/user identifier for namespacing
100
+ file_name: The name of the file to save
101
+ data: The file content as bytes
102
+ append: If True, append to existing file instead of overwriting.
103
+
104
+ Raises:
105
+ Exception: If the file cannot be saved
106
+ """
107
+ raise NotImplementedError
39
108
 
40
109
  @abstractmethod
41
110
  async def get_file(self, flow_id: str, file_name: str) -> bytes:
42
- """Retrieve a file."""
111
+ """Retrieve a file from storage.
112
+
113
+ Args:
114
+ flow_id: The flow/user identifier for namespacing
115
+ file_name: The name of the file to retrieve
116
+
117
+ Returns:
118
+ bytes: The file content
119
+
120
+ Raises:
121
+ FileNotFoundError: If the file does not exist
122
+ """
123
+ raise NotImplementedError
124
+
125
+ async def get_file_stream(self, flow_id: str, file_name: str, chunk_size: int = 8192) -> AsyncIterator[bytes]:
126
+ """Retrieve a file from storage as a stream.
127
+
128
+ Default implementation loads the entire file and yields it in chunks.
129
+ Subclasses can override this for more efficient streaming.
130
+
131
+ Args:
132
+ flow_id: The flow/user identifier for namespacing
133
+ file_name: The name of the file to retrieve
134
+ chunk_size: Size of chunks to yield (default: 8192 bytes)
135
+
136
+ Yields:
137
+ bytes: Chunks of the file content
138
+
139
+ Raises:
140
+ FileNotFoundError: If the file does not exist
141
+ """
142
+ # Default implementation - subclasses can override for true streaming
143
+ content = await self.get_file(flow_id, file_name)
144
+ for i in range(0, len(content), chunk_size):
145
+ yield content[i : i + chunk_size]
43
146
 
44
147
  @abstractmethod
45
148
  async def list_files(self, flow_id: str) -> list[str]:
46
- """List files in a flow."""
149
+ """List all files in a flow's storage namespace.
47
150
 
48
- @abstractmethod
49
- async def delete_file(self, flow_id: str, file_name: str) -> None:
50
- """Delete a file."""
151
+ Args:
152
+ flow_id: The flow/user identifier for namespacing
153
+
154
+ Returns:
155
+ list[str]: List of file names in the namespace
156
+
157
+ Raises:
158
+ FileNotFoundError: If the namespace directory does not exist
159
+ """
160
+ raise NotImplementedError
51
161
 
52
162
  @abstractmethod
53
163
  async def get_file_size(self, flow_id: str, file_name: str) -> int:
54
- """Get the size of a file."""
164
+ """Get the size of a file in bytes.
165
+
166
+ Args:
167
+ flow_id: The flow/user identifier for namespacing
168
+ file_name: The name of the file
169
+
170
+ Returns:
171
+ int: Size of the file in bytes
172
+
173
+ Raises:
174
+ FileNotFoundError: If the file does not exist
175
+ """
176
+ raise NotImplementedError
177
+
178
+ @abstractmethod
179
+ async def delete_file(self, flow_id: str, file_name: str) -> None:
180
+ """Delete a file from storage.
181
+
182
+ Args:
183
+ flow_id: The flow/user identifier for namespacing
184
+ file_name: The name of the file to delete
185
+
186
+ Note:
187
+ Should not raise an error if the file doesn't exist
188
+ """
189
+ raise NotImplementedError
190
+
191
+ async def teardown(self) -> None:
192
+ """Perform cleanup operations when the service is being shut down.
193
+
194
+ Subclasses should override this to clean up any resources (connections, etc.)
195
+ """
196
+ raise NotImplementedError
@@ -208,6 +208,9 @@ class Output(BaseModel):
208
208
  allows_loop: bool = Field(default=False)
209
209
  """Specifies if the output allows looping."""
210
210
 
211
+ loop_types: list[str] | None = Field(default=None)
212
+ """List of additional types to include for loop inputs when allows_loop is True."""
213
+
211
214
  group_outputs: bool = Field(default=False)
212
215
  """Specifies if all outputs should be grouped and shown without dropdowns."""
213
216
 
lfx/utils/image.py CHANGED
@@ -6,14 +6,19 @@ import base64
6
6
  from functools import lru_cache
7
7
  from pathlib import Path
8
8
 
9
+ from lfx.log import logger
10
+ from lfx.services.deps import get_storage_service
11
+ from lfx.utils.async_helpers import run_until_complete
9
12
  from lfx.utils.helpers import get_mime_type
10
13
 
11
14
 
12
15
  def convert_image_to_base64(image_path: str | Path) -> str:
13
16
  """Convert an image file to a base64 encoded string.
14
17
 
18
+ Handles both local files and S3 storage paths.
19
+
15
20
  Args:
16
- image_path: Path to the image file
21
+ image_path: Path to the image file (local or S3 path like "flow_id/filename")
17
22
 
18
23
  Returns:
19
24
  Base64 encoded string of the image
@@ -22,6 +27,20 @@ def convert_image_to_base64(image_path: str | Path) -> str:
22
27
  FileNotFoundError: If the image file doesn't exist
23
28
  """
24
29
  image_path = Path(image_path)
30
+
31
+ storage_service = get_storage_service()
32
+ if storage_service:
33
+ flow_id, file_name = storage_service.parse_file_path(str(image_path))
34
+ try:
35
+ file_content = run_until_complete(
36
+ storage_service.get_file(flow_id=flow_id, file_name=file_name) # type: ignore[call-arg]
37
+ )
38
+ return base64.b64encode(file_content).decode("utf-8")
39
+ except Exception as e:
40
+ logger.error(f"Error reading image file: {e}")
41
+ raise
42
+
43
+ # Fall back to local file access
25
44
  if not image_path.exists():
26
45
  msg = f"Image file not found: {image_path}"
27
46
  raise FileNotFoundError(msg)
@@ -34,7 +53,7 @@ def create_data_url(image_path: str | Path, mime_type: str | None = None) -> str
34
53
  """Create a data URL from an image file.
35
54
 
36
55
  Args:
37
- image_path: Path to the image file
56
+ image_path: Path to the image file (local or S3 path like "flow_id/filename")
38
57
  mime_type: MIME type of the image. If None, will be auto-detected
39
58
 
40
59
  Returns:
@@ -44,9 +63,6 @@ def create_data_url(image_path: str | Path, mime_type: str | None = None) -> str
44
63
  FileNotFoundError: If the image file doesn't exist
45
64
  """
46
65
  image_path = Path(image_path)
47
- if not image_path.exists():
48
- msg = f"Image file not found: {image_path}"
49
- raise FileNotFoundError(msg)
50
66
 
51
67
  if mime_type is None:
52
68
  mime_type = get_mime_type(image_path)
@@ -57,14 +73,16 @@ def create_data_url(image_path: str | Path, mime_type: str | None = None) -> str
57
73
 
58
74
  @lru_cache(maxsize=50)
59
75
  def create_image_content_dict(
60
- image_path: str | Path, mime_type: str | None = None, model_name: str | None = None
76
+ image_path: str | Path,
77
+ mime_type: str | None = None,
78
+ model_name: str | None = None, # noqa: ARG001
61
79
  ) -> dict:
62
80
  """Create a content dictionary for multimodal inputs from an image file.
63
81
 
64
82
  Args:
65
- image_path: Path to the image file
83
+ image_path: Path to the image file (local or S3 path like "flow_id/filename")
66
84
  mime_type: MIME type of the image. If None, will be auto-detected
67
- model_name: Optional model parameter to determine content dict structure
85
+ model_name: Optional model parameter (kept for backward compatibility, no longer used)
68
86
 
69
87
  Returns:
70
88
  Content dictionary with type and image_url fields
@@ -74,6 +92,6 @@ def create_image_content_dict(
74
92
  """
75
93
  data_url = create_data_url(image_path, mime_type)
76
94
 
77
- if model_name == "OllamaModel":
78
- return {"type": "image_url", "source_type": "url", "image_url": data_url}
79
- return {"type": "image", "source_type": "url", "url": data_url}
95
+ # Standard format for OpenAI, Anthropic, Gemini, and most providers
96
+ # Format: {"type": "image_url", "image_url": {"url": "data:..."}}
97
+ return {"type": "image_url", "image_url": {"url": data_url}}