blaxel 0.2.31__py3-none-any.whl → 0.2.31rc120__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 (174) hide show
  1. blaxel/__init__.py +3 -3
  2. blaxel/core/agents/__init__.py +6 -13
  3. blaxel/core/authentication/__init__.py +1 -2
  4. blaxel/core/authentication/devicemode.py +1 -9
  5. blaxel/core/authentication/oauth.py +6 -13
  6. blaxel/core/authentication/types.py +0 -1
  7. blaxel/core/cache/cache.py +3 -10
  8. blaxel/core/client/api/agents/list_agent_revisions.py +1 -3
  9. blaxel/core/client/api/compute/delete_sandbox_preview_token.py +2 -6
  10. blaxel/core/client/api/compute/start_sandbox.py +1 -3
  11. blaxel/core/client/api/compute/stop_sandbox.py +1 -3
  12. blaxel/core/client/api/default/list_sandbox_hub_definitions.py +2 -6
  13. blaxel/core/client/api/functions/list_function_revisions.py +1 -3
  14. blaxel/core/client/api/images/cleanup_images.py +1 -3
  15. blaxel/core/client/api/integrations/list_integration_connections.py +2 -6
  16. blaxel/core/client/api/invitations/list_all_pending_invitations.py +1 -3
  17. blaxel/core/client/api/jobs/create_job_execution.py +1 -3
  18. blaxel/core/client/api/jobs/delete_job_execution.py +1 -3
  19. blaxel/core/client/api/jobs/get_job_execution.py +1 -3
  20. blaxel/core/client/api/jobs/list_job_executions.py +2 -6
  21. blaxel/core/client/api/jobs/list_job_revisions.py +1 -3
  22. blaxel/core/client/api/locations/list_locations.py +1 -3
  23. blaxel/core/client/api/models/list_model_revisions.py +1 -3
  24. blaxel/core/client/api/service_accounts/create_workspace_service_account.py +2 -6
  25. blaxel/core/client/api/service_accounts/delete_workspace_service_account.py +2 -6
  26. blaxel/core/client/api/service_accounts/get_workspace_service_accounts.py +1 -3
  27. blaxel/core/client/api/service_accounts/update_workspace_service_account.py +2 -6
  28. blaxel/core/client/api/volume_templates/list_volume_templates.py +1 -3
  29. blaxel/core/client/api/workspaces/accept_workspace_invitation.py +2 -6
  30. blaxel/core/client/api/workspaces/invite_workspace_user.py +2 -6
  31. blaxel/core/client/api/workspaces/update_workspace_user_role.py +2 -6
  32. blaxel/core/client/client.py +19 -5
  33. blaxel/core/client/models/agent.py +4 -11
  34. blaxel/core/client/models/agent_spec.py +5 -18
  35. blaxel/core/client/models/billable_time_metric.py +1 -0
  36. blaxel/core/client/models/configuration.py +1 -0
  37. blaxel/core/client/models/core_spec.py +3 -10
  38. blaxel/core/client/models/core_spec_configurations.py +1 -0
  39. blaxel/core/client/models/create_job_execution_request.py +1 -0
  40. blaxel/core/client/models/create_job_execution_response.py +1 -0
  41. blaxel/core/client/models/custom_domain.py +2 -5
  42. blaxel/core/client/models/custom_domain_metadata.py +1 -0
  43. blaxel/core/client/models/custom_domain_spec.py +2 -5
  44. blaxel/core/client/models/delete_volume_template_version_response_200.py +2 -5
  45. blaxel/core/client/models/entrypoint.py +1 -0
  46. blaxel/core/client/models/form.py +2 -5
  47. blaxel/core/client/models/function.py +4 -11
  48. blaxel/core/client/models/function_spec.py +4 -13
  49. blaxel/core/client/models/image.py +2 -5
  50. blaxel/core/client/models/image_spec.py +1 -0
  51. blaxel/core/client/models/integration.py +3 -10
  52. blaxel/core/client/models/integration_connection.py +2 -5
  53. blaxel/core/client/models/integration_connection_spec.py +1 -0
  54. blaxel/core/client/models/integration_endpoint.py +2 -5
  55. blaxel/core/client/models/integration_endpoints.py +2 -0
  56. blaxel/core/client/models/job.py +4 -11
  57. blaxel/core/client/models/job_execution.py +2 -5
  58. blaxel/core/client/models/job_execution_spec.py +1 -0
  59. blaxel/core/client/models/job_execution_task.py +2 -5
  60. blaxel/core/client/models/job_metrics.py +2 -5
  61. blaxel/core/client/models/job_spec.py +4 -13
  62. blaxel/core/client/models/jobs_network_chart.py +1 -0
  63. blaxel/core/client/models/jobs_success_failed_chart.py +3 -10
  64. blaxel/core/client/models/latency_metric.py +2 -5
  65. blaxel/core/client/models/location_response.py +1 -0
  66. blaxel/core/client/models/mcp_definition.py +2 -5
  67. blaxel/core/client/models/metadata.py +1 -0
  68. blaxel/core/client/models/metrics.py +4 -11
  69. blaxel/core/client/models/model.py +4 -11
  70. blaxel/core/client/models/model_spec.py +3 -10
  71. blaxel/core/client/models/pending_invitation_accept.py +2 -5
  72. blaxel/core/client/models/pending_invitation_render.py +3 -10
  73. blaxel/core/client/models/policy.py +2 -5
  74. blaxel/core/client/models/policy_spec.py +4 -11
  75. blaxel/core/client/models/preview.py +2 -5
  76. blaxel/core/client/models/preview_spec.py +1 -0
  77. blaxel/core/client/models/preview_token.py +2 -5
  78. blaxel/core/client/models/public_ips.py +1 -0
  79. blaxel/core/client/models/request_duration_over_time_metrics.py +1 -0
  80. blaxel/core/client/models/request_total_by_origin_metric.py +7 -16
  81. blaxel/core/client/models/request_total_metric.py +3 -8
  82. blaxel/core/client/models/resource_metrics.py +17 -58
  83. blaxel/core/client/models/runtime.py +1 -0
  84. blaxel/core/client/models/sandbox.py +4 -11
  85. blaxel/core/client/models/sandbox_definition.py +1 -0
  86. blaxel/core/client/models/sandbox_lifecycle.py +1 -0
  87. blaxel/core/client/models/sandbox_spec.py +6 -21
  88. blaxel/core/client/models/serverless_config.py +1 -0
  89. blaxel/core/client/models/start_sandbox.py +2 -5
  90. blaxel/core/client/models/stop_sandbox.py +2 -5
  91. blaxel/core/client/models/store_agent.py +1 -0
  92. blaxel/core/client/models/store_configuration.py +1 -0
  93. blaxel/core/client/models/template.py +1 -0
  94. blaxel/core/client/models/time_to_first_token_over_time_metrics.py +2 -3
  95. blaxel/core/client/models/token_rate_metrics.py +1 -0
  96. blaxel/core/client/models/trigger.py +1 -0
  97. blaxel/core/client/models/trigger_configuration.py +1 -0
  98. blaxel/core/client/models/volume.py +4 -11
  99. blaxel/core/client/models/volume_template.py +2 -5
  100. blaxel/core/client/models/workspace.py +2 -5
  101. blaxel/core/client/response_interceptor.py +1 -3
  102. blaxel/core/common/autoload.py +11 -9
  103. blaxel/core/common/env.py +8 -10
  104. blaxel/core/common/settings.py +2 -4
  105. blaxel/core/common/webhook.py +1 -0
  106. blaxel/core/jobs/__init__.py +3 -13
  107. blaxel/core/mcp/client.py +2 -8
  108. blaxel/core/mcp/server.py +2 -8
  109. blaxel/core/models/__init__.py +5 -6
  110. blaxel/core/sandbox/__init__.py +1 -1
  111. blaxel/core/sandbox/client/api/codegen/get_codegen_reranking_path.py +2 -6
  112. blaxel/core/sandbox/client/api/fastapply/put_codegen_fastapply_path.py +2 -6
  113. blaxel/core/sandbox/client/api/filesystem/delete_filesystem_multipart_upload_id_abort.py +2 -6
  114. blaxel/core/sandbox/client/api/filesystem/delete_filesystem_path.py +2 -6
  115. blaxel/core/sandbox/client/api/filesystem/delete_filesystem_tree_path.py +2 -6
  116. blaxel/core/sandbox/client/api/filesystem/get_filesystem_content_search_path.py +1 -3
  117. blaxel/core/sandbox/client/api/filesystem/get_filesystem_find_path.py +2 -6
  118. blaxel/core/sandbox/client/api/filesystem/get_filesystem_search_path.py +2 -6
  119. blaxel/core/sandbox/client/api/filesystem/get_watch_filesystem_path.py +2 -6
  120. blaxel/core/sandbox/client/api/filesystem/post_filesystem_multipart_upload_id_complete.py +2 -6
  121. blaxel/core/sandbox/client/api/filesystem/put_filesystem_path.py +2 -6
  122. blaxel/core/sandbox/client/api/process/delete_process_identifier.py +2 -6
  123. blaxel/core/sandbox/client/api/process/delete_process_identifier_kill.py +2 -6
  124. blaxel/core/sandbox/client/api/process/get_process.py +1 -3
  125. blaxel/core/sandbox/client/api/process/get_process_identifier.py +2 -6
  126. blaxel/core/sandbox/client/api/process/get_process_identifier_logs.py +2 -6
  127. blaxel/core/sandbox/client/api/process/get_process_identifier_logs_stream.py +2 -6
  128. blaxel/core/sandbox/client/api/process/post_process.py +2 -6
  129. blaxel/core/sandbox/client/client.py +1 -3
  130. blaxel/core/sandbox/client/models/filesystem_multipart_upload_parts.py +1 -3
  131. blaxel/core/sandbox/default/__init__.py +1 -0
  132. blaxel/core/sandbox/default/action.py +3 -3
  133. blaxel/core/sandbox/default/codegen.py +4 -2
  134. blaxel/core/sandbox/default/filesystem.py +82 -38
  135. blaxel/core/sandbox/default/interpreter.py +10 -17
  136. blaxel/core/sandbox/default/preview.py +2 -6
  137. blaxel/core/sandbox/default/process.py +7 -25
  138. blaxel/core/sandbox/default/sandbox.py +2 -7
  139. blaxel/core/sandbox/sync/__init__.py +2 -0
  140. blaxel/core/sandbox/sync/action.py +3 -2
  141. blaxel/core/sandbox/sync/codegen.py +5 -1
  142. blaxel/core/sandbox/sync/filesystem.py +6 -17
  143. blaxel/core/sandbox/sync/interpreter.py +6 -10
  144. blaxel/core/sandbox/sync/network.py +2 -0
  145. blaxel/core/sandbox/sync/preview.py +9 -21
  146. blaxel/core/sandbox/sync/process.py +8 -32
  147. blaxel/core/sandbox/sync/sandbox.py +6 -13
  148. blaxel/core/sandbox/sync/session.py +4 -6
  149. blaxel/core/sandbox/types.py +1 -2
  150. blaxel/core/tools/__init__.py +6 -30
  151. blaxel/core/tools/common.py +1 -1
  152. blaxel/core/tools/types.py +1 -2
  153. blaxel/crewai/model.py +5 -20
  154. blaxel/googleadk/__init__.py +1 -1
  155. blaxel/googleadk/tools.py +5 -3
  156. blaxel/langgraph/custom/gemini.py +133 -126
  157. blaxel/langgraph/model.py +50 -54
  158. blaxel/langgraph/tools.py +3 -9
  159. blaxel/llamaindex/custom/cohere.py +16 -25
  160. blaxel/llamaindex/model.py +57 -44
  161. blaxel/llamaindex/tools.py +3 -2
  162. blaxel/pydantic/custom/gemini.py +3 -3
  163. blaxel/pydantic/tools.py +4 -2
  164. blaxel/telemetry/exporters.py +3 -10
  165. blaxel/telemetry/instrumentation/blaxel_langgraph.py +2 -4
  166. blaxel/telemetry/instrumentation/blaxel_langgraph_gemini.py +5 -22
  167. blaxel/telemetry/instrumentation/utils.py +3 -3
  168. blaxel/telemetry/log/log.py +3 -2
  169. blaxel/telemetry/log/logger.py +15 -21
  170. blaxel/telemetry/span.py +6 -10
  171. {blaxel-0.2.31.dist-info → blaxel-0.2.31rc120.dist-info}/METADATA +2 -2
  172. {blaxel-0.2.31.dist-info → blaxel-0.2.31rc120.dist-info}/RECORD +174 -174
  173. {blaxel-0.2.31.dist-info → blaxel-0.2.31rc120.dist-info}/WHEEL +0 -0
  174. {blaxel-0.2.31.dist-info → blaxel-0.2.31rc120.dist-info}/licenses/LICENSE +0 -0
@@ -23,15 +23,9 @@ from typing import (
23
23
 
24
24
  import httpx
25
25
  import requests
26
- from langchain_core.callbacks.manager import (
27
- AsyncCallbackManagerForLLMRun,
28
- CallbackManagerForLLMRun,
29
- )
26
+ from langchain_core.callbacks.manager import AsyncCallbackManagerForLLMRun, CallbackManagerForLLMRun
30
27
  from langchain_core.language_models import LanguageModelInput
31
- from langchain_core.language_models.chat_models import (
32
- BaseChatModel,
33
- LangSmithParams,
34
- )
28
+ from langchain_core.language_models.chat_models import BaseChatModel, LangSmithParams
35
29
  from langchain_core.messages import (
36
30
  AIMessage,
37
31
  AIMessageChunk,
@@ -42,21 +36,13 @@ from langchain_core.messages import (
42
36
  ToolMessage,
43
37
  )
44
38
  from langchain_core.messages.ai import UsageMetadata
45
- from langchain_core.messages.tool import (
46
- invalid_tool_call,
47
- tool_call,
48
- tool_call_chunk,
49
- )
39
+ from langchain_core.messages.tool import invalid_tool_call, tool_call, tool_call_chunk
50
40
  from langchain_core.output_parsers.openai_tools import (
51
41
  JsonOutputKeyToolsParser,
52
42
  PydanticToolsParser,
53
43
  parse_tool_calls,
54
44
  )
55
- from langchain_core.outputs import (
56
- ChatGeneration,
57
- ChatGenerationChunk,
58
- ChatResult,
59
- )
45
+ from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
60
46
  from langchain_core.runnables import Runnable, RunnablePassthrough
61
47
  from langchain_core.tools import BaseTool
62
48
  from langchain_core.utils.function_calling import convert_to_openai_tool
@@ -87,7 +73,6 @@ _ToolDict = Dict[str, Any]
87
73
  SafetySettingDict = Dict[str, str]
88
74
  OutputParserLike = Union[PydanticToolsParser, JsonOutputKeyToolsParser]
89
75
 
90
-
91
76
  # Data classes
92
77
  class Part(BaseModel):
93
78
  text: str | None = None
@@ -96,47 +81,38 @@ class Part(BaseModel):
96
81
  function_call: Dict[str, Any] | None = None
97
82
  function_response: Dict[str, Any] | None = None
98
83
 
99
-
100
84
  class Content(BaseModel):
101
85
  role: str | None = None
102
86
  parts: List[Part]
103
87
 
104
-
105
88
  class Blob(BaseModel):
106
89
  data: str
107
90
  mime_type: str
108
91
 
109
-
110
92
  class FileData(BaseModel):
111
93
  file_uri: str
112
94
  mime_type: str
113
95
 
114
-
115
96
  class VideoMetadata(BaseModel):
116
97
  duration: str | None = None
117
98
  start_offset: str | None = None
118
99
  end_offset: str | None = None
119
100
 
120
-
121
101
  class FunctionCall(BaseModel):
122
102
  name: str
123
103
  args: Dict[str, Any]
124
104
 
125
-
126
105
  class FunctionResponse(BaseModel):
127
106
  name: str
128
107
  response: Dict[str, Any]
129
108
 
130
-
131
109
  class SafetySetting(BaseModel):
132
110
  category: str
133
111
  threshold: str
134
112
 
135
-
136
113
  class ToolConfig(BaseModel):
137
114
  function_calling_config: Dict[str, Any]
138
115
 
139
-
140
116
  class GenerationConfig(BaseModel):
141
117
  candidate_count: int | None = None
142
118
  temperature: float | None = None
@@ -146,13 +122,11 @@ class GenerationConfig(BaseModel):
146
122
  top_p: float | None = None
147
123
  response_modalities: List[str] | None = None
148
124
 
149
-
150
125
  class GoogleTool(BaseModel):
151
126
  name: str
152
127
  description: str
153
128
  parameters: Dict[str, Any]
154
129
 
155
-
156
130
  class ImageBytesLoader:
157
131
  def load_part(self, image_url: str) -> Part:
158
132
  """Load an image from a URL and convert it to a Part."""
@@ -164,17 +138,16 @@ class ImageBytesLoader:
164
138
  # Convert to JPEG format
165
139
  img = Image.open(io.BytesIO(response.content))
166
140
  img_byte_arr = io.BytesIO()
167
- img.save(img_byte_arr, format="JPEG")
141
+ img.save(img_byte_arr, format='JPEG')
168
142
  img_byte_arr = img_byte_arr.getvalue()
169
143
 
170
144
  return Part(
171
145
  inline_data={
172
146
  "mime_type": "image/jpeg",
173
- "data": img_byte_arr.decode("utf-8"),
147
+ "data": img_byte_arr.decode('utf-8')
174
148
  }
175
149
  )
176
150
 
177
-
178
151
  class ChatGoogleGenerativeAIError(Exception):
179
152
  """
180
153
  Custom exception class for errors associated with the `Google GenAI` API.
@@ -183,7 +156,6 @@ class ChatGoogleGenerativeAIError(Exception):
183
156
  Google genai API usage in the ChatGoogleGenerativeAI class, such as unsupported
184
157
  message types or roles.
185
158
  """
186
-
187
159
  pass
188
160
 
189
161
 
@@ -238,8 +210,8 @@ def _chat_with_retry(generation_method: Callable, **kwargs: Any) -> Any:
238
210
  def _chat_with_retry(**kwargs: Any) -> Any:
239
211
  try:
240
212
  # Extract request parameters and other kwargs
241
- request = kwargs.pop("request", {})
242
- kwargs.pop("metadata", None)
213
+ request = kwargs.pop('request', {})
214
+ kwargs.pop('metadata', None)
243
215
 
244
216
  # Unpack request parameters into kwargs
245
217
  kwargs.update(request)
@@ -252,10 +224,14 @@ def _chat_with_retry(generation_method: Callable, **kwargs: Any) -> Any:
252
224
  )
253
225
  raise ValueError(error_msg)
254
226
  elif e.response.status_code == 403: # Forbidden
255
- error_msg = "Access forbidden. Please check your API key and permissions."
227
+ error_msg = (
228
+ "Access forbidden. Please check your API key and permissions."
229
+ )
256
230
  raise ValueError(error_msg)
257
231
  else:
258
- raise ChatGoogleGenerativeAIError(f"HTTP error occurred: {e.response.text}") from e
232
+ raise ChatGoogleGenerativeAIError(
233
+ f"HTTP error occurred: {e.response.text}"
234
+ ) from e
259
235
  except Exception as e:
260
236
  raise e
261
237
 
@@ -283,8 +259,8 @@ async def _achat_with_retry(generation_method: Callable, **kwargs: Any) -> Any:
283
259
  async def _achat_with_retry(**kwargs: Any) -> Any:
284
260
  try:
285
261
  # Extract request parameters and other kwargs
286
- request = kwargs.pop("request", {})
287
- kwargs.pop("metadata", None)
262
+ request = kwargs.pop('request', {})
263
+ kwargs.pop('metadata', None)
288
264
 
289
265
  # Unpack request parameters into kwargs
290
266
  kwargs.update(request)
@@ -297,10 +273,14 @@ async def _achat_with_retry(generation_method: Callable, **kwargs: Any) -> Any:
297
273
  )
298
274
  raise ValueError(error_msg)
299
275
  elif e.response.status_code == 403: # Forbidden
300
- error_msg = "Access forbidden. Please check your API key and permissions."
276
+ error_msg = (
277
+ "Access forbidden. Please check your API key and permissions."
278
+ )
301
279
  raise ValueError(error_msg)
302
280
  else:
303
- raise ChatGoogleGenerativeAIError(f"HTTP error occurred: {e.response.text}") from e
281
+ raise ChatGoogleGenerativeAIError(
282
+ f"HTTP error occurred: {e.response.text}"
283
+ ) from e
304
284
  except Exception as e:
305
285
  raise e
306
286
 
@@ -330,7 +310,9 @@ def _convert_to_parts(
330
310
  img_url = part["image_url"]
331
311
  if isinstance(img_url, dict):
332
312
  if "url" not in img_url:
333
- raise ValueError(f"Unrecognized message image format: {img_url}")
313
+ raise ValueError(
314
+ f"Unrecognized message image format: {img_url}"
315
+ )
334
316
  img_url = img_url["url"]
335
317
  parts.append(image_loader.load_part(img_url))
336
318
  # Handle media type like LangChain.js
@@ -342,13 +324,17 @@ def _convert_to_parts(
342
324
  media_part = Part()
343
325
 
344
326
  if "data" in part:
345
- media_part.inline_data = Blob(data=part["data"], mime_type=mime_type)
327
+ media_part.inline_data = Blob(
328
+ data=part["data"], mime_type=mime_type
329
+ )
346
330
  elif "file_uri" in part:
347
331
  media_part.file_data = FileData(
348
332
  file_uri=part["file_uri"], mime_type=mime_type
349
333
  )
350
334
  else:
351
- raise ValueError(f"Media part must have either data or file_uri: {part}")
335
+ raise ValueError(
336
+ f"Media part must have either data or file_uri: {part}"
337
+ )
352
338
  parts.append(media_part)
353
339
  else:
354
340
  raise ValueError(
@@ -357,12 +343,16 @@ def _convert_to_parts(
357
343
  )
358
344
  else:
359
345
  # Yolo
360
- logger.warning("Unrecognized message part format. Assuming it's a text part.")
346
+ logger.warning(
347
+ "Unrecognized message part format. Assuming it's a text part."
348
+ )
361
349
  parts.append(Part(text=str(part)))
362
350
  else:
363
351
  # TODO: Maybe some of Google's native stuff
364
352
  # would hit this branch.
365
- raise ChatGoogleGenerativeAIError("Gemini only supports text and inline_data parts.")
353
+ raise ChatGoogleGenerativeAIError(
354
+ "Gemini only supports text and inline_data parts."
355
+ )
366
356
  return parts
367
357
 
368
358
 
@@ -414,8 +404,7 @@ def _get_ai_message_tool_messages_parts(
414
404
 
415
405
 
416
406
  def _parse_chat_history(
417
- input_messages: Sequence[BaseMessage],
418
- convert_system_message_to_human: bool = False,
407
+ input_messages: Sequence[BaseMessage], convert_system_message_to_human: bool = False
419
408
  ) -> Tuple[Content | None, List[Content]]:
420
409
  messages: List[Content] = []
421
410
 
@@ -426,7 +415,9 @@ def _parse_chat_history(
426
415
  messages_without_tool_messages = [
427
416
  message for message in input_messages if not isinstance(message, ToolMessage)
428
417
  ]
429
- tool_messages = [message for message in input_messages if isinstance(message, ToolMessage)]
418
+ tool_messages = [
419
+ message for message in input_messages if isinstance(message, ToolMessage)
420
+ ]
430
421
  for i, message in enumerate(messages_without_tool_messages):
431
422
  if isinstance(message, SystemMessage):
432
423
  system_parts = _convert_to_parts(message.content)
@@ -471,7 +462,9 @@ def _parse_chat_history(
471
462
  role = "user"
472
463
  parts = [_convert_tool_message_to_part(message)]
473
464
  else:
474
- raise ValueError(f"Unexpected message with type {type(message)} at the position {i}.")
465
+ raise ValueError(
466
+ f"Unexpected message with type {type(message)} at the position {i}."
467
+ )
475
468
 
476
469
  messages.append(Content(role=role, parts=parts))
477
470
  return system_instruction, messages
@@ -630,7 +623,9 @@ def _response_to_result(
630
623
  )
631
624
  )
632
625
  else:
633
- generations.append(ChatGeneration(message=message, generation_info=generation_info))
626
+ generations.append(
627
+ ChatGeneration(message=message, generation_info=generation_info)
628
+ )
634
629
  if not response.get("candidates"):
635
630
  # Likely a "prompt feedback" violation (e.g., toxic input)
636
631
  # Raising an error would be different than how OpenAI handles it,
@@ -641,7 +636,9 @@ def _response_to_result(
641
636
  )
642
637
  if stream:
643
638
  generations = [
644
- ChatGenerationChunk(message=AIMessageChunk(content=""), generation_info={})
639
+ ChatGenerationChunk(
640
+ message=AIMessageChunk(content=""), generation_info={}
641
+ )
645
642
  ]
646
643
  else:
647
644
  generations = [ChatGeneration(message=AIMessage(""), generation_info={})]
@@ -699,7 +696,7 @@ class GeminiRestClient:
699
696
 
700
697
  def _convert_to_dict(self, obj: Any) -> Any:
701
698
  """Convert Pydantic models and other objects to dictionaries."""
702
- if hasattr(obj, "model_dump"):
699
+ if hasattr(obj, 'model_dump'):
703
700
  return obj.model_dump()
704
701
  elif isinstance(obj, list):
705
702
  return [self._convert_to_dict(item) for item in obj]
@@ -813,6 +810,7 @@ class GeminiRestClient:
813
810
  cached_content=cached_content,
814
811
  )
815
812
 
813
+
816
814
  with self._get_client().stream(
817
815
  "POST",
818
816
  f"v1beta/{model}:streamGenerateContent",
@@ -929,9 +927,7 @@ class ChatGoogleGenerativeAI(BaseChatModel):
929
927
  else:
930
928
  google_api_key = self.google_api_key
931
929
 
932
- base_url = self.client_options.get(
933
- "api_endpoint", "https://generativelanguage.googleapis.com"
934
- )
930
+ base_url = self.client_options.get("api_endpoint", "https://generativelanguage.googleapis.com")
935
931
  self.client = GeminiRestClient(
936
932
  api_key=google_api_key,
937
933
  base_url=base_url,
@@ -944,7 +940,9 @@ class ChatGoogleGenerativeAI(BaseChatModel):
944
940
  )
945
941
  return self
946
942
 
947
- def _get_ls_params(self, stop: List[str] | None = None, **kwargs: Any) -> LangSmithParams:
943
+ def _get_ls_params(
944
+ self, stop: List[str] | None = None, **kwargs: Any
945
+ ) -> LangSmithParams:
948
946
  """Get standard params for tracing."""
949
947
  params = self._get_invocation_params(stop=stop, **kwargs)
950
948
  ls_params = LangSmithParams(
@@ -1084,11 +1082,15 @@ class ChatGoogleGenerativeAI(BaseChatModel):
1084
1082
 
1085
1083
  prev_usage_metadata: UsageMetadata | None = None
1086
1084
  for chunk in response:
1087
- _chat_result = _response_to_result(chunk, stream=True, prev_usage=prev_usage_metadata)
1085
+ _chat_result = _response_to_result(
1086
+ chunk, stream=True, prev_usage=prev_usage_metadata
1087
+ )
1088
1088
  gen = cast(ChatGenerationChunk, _chat_result.generations[0])
1089
1089
  message = cast(AIMessageChunk, gen.message)
1090
1090
 
1091
- curr_usage_metadata: UsageMetadata | dict[str, int] = message.usage_metadata or {}
1091
+ curr_usage_metadata: UsageMetadata | dict[str, int] = (
1092
+ message.usage_metadata or {}
1093
+ )
1092
1094
 
1093
1095
  prev_usage_metadata = (
1094
1096
  message.usage_metadata
@@ -1140,11 +1142,15 @@ class ChatGoogleGenerativeAI(BaseChatModel):
1140
1142
  **kwargs,
1141
1143
  metadata=self.default_metadata,
1142
1144
  ):
1143
- _chat_result = _response_to_result(chunk, stream=True, prev_usage=prev_usage_metadata)
1145
+ _chat_result = _response_to_result(
1146
+ chunk, stream=True, prev_usage=prev_usage_metadata
1147
+ )
1144
1148
  gen = cast(ChatGenerationChunk, _chat_result.generations[0])
1145
1149
  message = cast(AIMessageChunk, gen.message)
1146
1150
 
1147
- curr_usage_metadata: UsageMetadata | dict[str, int] = message.usage_metadata or {}
1151
+ curr_usage_metadata: UsageMetadata | dict[str, int] = (
1152
+ message.usage_metadata or {}
1153
+ )
1148
1154
 
1149
1155
  prev_usage_metadata = (
1150
1156
  message.usage_metadata
@@ -1190,7 +1196,9 @@ class ChatGoogleGenerativeAI(BaseChatModel):
1190
1196
  filtered_messages = []
1191
1197
  for message in messages:
1192
1198
  if isinstance(message, HumanMessage) and not message.content:
1193
- warnings.warn("HumanMessage with empty content was removed to prevent API error")
1199
+ warnings.warn(
1200
+ "HumanMessage with empty content was removed to prevent API error"
1201
+ )
1194
1202
  else:
1195
1203
  filtered_messages.append(message)
1196
1204
  messages = filtered_messages
@@ -1206,7 +1214,9 @@ class ChatGoogleGenerativeAI(BaseChatModel):
1206
1214
  f"be specified if 'tools' is specified."
1207
1215
  )
1208
1216
  raise ValueError(msg)
1209
- all_names = [f.name for t in formatted_tools for f in t.function_declarations]
1217
+ all_names = [
1218
+ f.name for t in formatted_tools for f in t.function_declarations
1219
+ ]
1210
1220
  tool_config = _tool_choice_to_tool_config(tool_choice, all_names)
1211
1221
 
1212
1222
  formatted_tool_config = None
@@ -1217,7 +1227,8 @@ class ChatGoogleGenerativeAI(BaseChatModel):
1217
1227
  formatted_safety_settings = []
1218
1228
  if safety_settings:
1219
1229
  formatted_safety_settings = [
1220
- SafetySetting(category=c, threshold=t) for c, t in safety_settings.items()
1230
+ SafetySetting(category=c, threshold=t)
1231
+ for c, t in safety_settings.items()
1221
1232
  ]
1222
1233
 
1223
1234
  # Construct the full model path
@@ -1229,7 +1240,9 @@ class ChatGoogleGenerativeAI(BaseChatModel):
1229
1240
  "tools": formatted_tools,
1230
1241
  "tool_config": formatted_tool_config,
1231
1242
  "safety_settings": formatted_safety_settings,
1232
- "generation_config": self._prepare_params(stop, generation_config=generation_config),
1243
+ "generation_config": self._prepare_params(
1244
+ stop, generation_config=generation_config
1245
+ ),
1233
1246
  "cached_content": cached_content,
1234
1247
  }
1235
1248
  if system_instruction:
@@ -1267,7 +1280,9 @@ class ChatGoogleGenerativeAI(BaseChatModel):
1267
1280
  raise ValueError(f"Received unsupported arguments {kwargs}")
1268
1281
  tool_name = _get_tool_name(schema) # type: ignore[arg-type]
1269
1282
  if isinstance(schema, type) and is_basemodel_subclass_safe(schema):
1270
- parser: OutputParserLike = PydanticToolsParser(tools=[schema], first_tool_only=True)
1283
+ parser: OutputParserLike = PydanticToolsParser(
1284
+ tools=[schema], first_tool_only=True
1285
+ )
1271
1286
  else:
1272
1287
  global WARNED_STRUCTURED_OUTPUT_JSON_MODE
1273
1288
  warnings.warn(
@@ -1303,7 +1318,9 @@ class ChatGoogleGenerativeAI(BaseChatModel):
1303
1318
 
1304
1319
  def bind_tools(
1305
1320
  self,
1306
- tools: Sequence[dict[str, Any] | type | Callable[..., Any] | BaseTool | GoogleTool],
1321
+ tools: Sequence[
1322
+ dict[str, Any] | type | Callable[..., Any] | BaseTool | GoogleTool
1323
+ ],
1307
1324
  tool_config: Union[Dict, _ToolConfigDict] | None = None,
1308
1325
  *,
1309
1326
  tool_choice: Union[_ToolChoiceType, bool] | None = None,
@@ -1329,7 +1346,9 @@ class ChatGoogleGenerativeAI(BaseChatModel):
1329
1346
  try:
1330
1347
  formatted_tools: list = [convert_to_openai_tool(tool) for tool in tools] # type: ignore[arg-type]
1331
1348
  except Exception:
1332
- formatted_tools = [tool_to_dict(convert_to_genai_function_declarations(tools))]
1349
+ formatted_tools = [
1350
+ tool_to_dict(convert_to_genai_function_declarations(tools))
1351
+ ]
1333
1352
  if tool_choice:
1334
1353
  kwargs["tool_choice"] = tool_choice
1335
1354
  elif tool_config:
@@ -1359,7 +1378,6 @@ def _get_tool_name(
1359
1378
  else:
1360
1379
  raise e
1361
1380
 
1362
-
1363
1381
  def _tool_choice_to_tool_config(
1364
1382
  tool_choice: Union[str, bool, Dict[str, Any]], all_names: List[str]
1365
1383
  ) -> Dict[str, Any]:
@@ -1372,7 +1390,9 @@ def _tool_choice_to_tool_config(
1372
1390
  }
1373
1391
  elif isinstance(tool_choice, str):
1374
1392
  if tool_choice not in all_names:
1375
- raise ValueError(f"Tool choice {tool_choice} not found in available tools: {all_names}")
1393
+ raise ValueError(
1394
+ f"Tool choice {tool_choice} not found in available tools: {all_names}"
1395
+ )
1376
1396
  return {
1377
1397
  "function_calling_config": {
1378
1398
  "mode": "ANY",
@@ -1382,9 +1402,8 @@ def _tool_choice_to_tool_config(
1382
1402
  else:
1383
1403
  return tool_choice
1384
1404
 
1385
-
1386
1405
  def convert_to_genai_function_declarations(
1387
- tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable[..., Any], BaseTool]],
1406
+ tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable[..., Any], BaseTool]]
1388
1407
  ) -> Dict[str, Any]:
1389
1408
  """Convert tools to Gemini function declarations format."""
1390
1409
  function_declarations = []
@@ -1392,64 +1411,55 @@ def convert_to_genai_function_declarations(
1392
1411
  if isinstance(tool, dict):
1393
1412
  fn = tool.get("function", {})
1394
1413
  fn_parameters = fn.get("parameters", {})
1395
- function_declarations.append(
1396
- {
1397
- "name": fn.get("name", ""),
1398
- "description": fn.get("description", ""),
1399
- "parameters": {
1400
- "type": "object",
1401
- "properties": fn_parameters.get("properties", {}),
1402
- "required": fn_parameters.get("required", []),
1403
- },
1404
- }
1405
- )
1414
+ function_declarations.append({
1415
+ "name": fn.get("name", ""),
1416
+ "description": fn.get("description", ""),
1417
+ "parameters": {
1418
+ "type": "object",
1419
+ "properties": fn_parameters.get("properties", {}),
1420
+ "required": fn_parameters.get("required", []),
1421
+ },
1422
+ })
1406
1423
  elif isinstance(tool, type) and issubclass(tool, BaseModel):
1407
1424
  schema = tool.model_json_schema()
1408
- function_declarations.append(
1409
- {
1410
- "name": schema.get("title", ""),
1411
- "description": schema.get("description", ""),
1412
- "parameters": {
1413
- "type": "object",
1414
- "properties": {
1415
- name: {
1416
- "type": prop.get("type", "string"),
1417
- "description": prop.get("description", ""),
1418
- }
1419
- for name, prop in schema.get("properties", {}).items()
1420
- },
1421
- "required": schema.get("required", []),
1425
+ function_declarations.append({
1426
+ "name": schema.get("title", ""),
1427
+ "description": schema.get("description", ""),
1428
+ "parameters": {
1429
+ "type": "object",
1430
+ "properties": {
1431
+ name: {
1432
+ "type": prop.get("type", "string"),
1433
+ "description": prop.get("description", ""),
1434
+ }
1435
+ for name, prop in schema.get("properties", {}).items()
1422
1436
  },
1423
- }
1424
- )
1437
+ "required": schema.get("required", []),
1438
+ },
1439
+ })
1425
1440
  elif callable(tool):
1426
1441
  # For callables, we'll create a basic function declaration
1427
- function_declarations.append(
1428
- {
1429
- "name": tool.__name__,
1430
- "description": tool.__doc__ or "",
1431
- "parameters": {
1432
- "type": "object",
1433
- "properties": {},
1434
- "required": [],
1435
- },
1436
- }
1437
- )
1442
+ function_declarations.append({
1443
+ "name": tool.__name__,
1444
+ "description": tool.__doc__ or "",
1445
+ "parameters": {
1446
+ "type": "object",
1447
+ "properties": {},
1448
+ "required": [],
1449
+ },
1450
+ })
1438
1451
  elif isinstance(tool, BaseTool):
1439
- function_declarations.append(
1440
- {
1441
- "name": tool.name,
1442
- "description": tool.description,
1443
- "parameters": {
1444
- "type": "object",
1445
- "properties": {},
1446
- "required": [],
1447
- },
1448
- }
1449
- )
1452
+ function_declarations.append({
1453
+ "name": tool.name,
1454
+ "description": tool.description,
1455
+ "parameters": {
1456
+ "type": "object",
1457
+ "properties": {},
1458
+ "required": [],
1459
+ },
1460
+ })
1450
1461
  return {"functionDeclarations": function_declarations}
1451
1462
 
1452
-
1453
1463
  def is_basemodel_subclass_safe(cls: Type[Any]) -> bool:
1454
1464
  """Check if a class is a safe subclass of BaseModel."""
1455
1465
  try:
@@ -1457,14 +1467,11 @@ def is_basemodel_subclass_safe(cls: Type[Any]) -> bool:
1457
1467
  except TypeError:
1458
1468
  return False
1459
1469
 
1460
-
1461
1470
  def tool_to_dict(tool: Dict[str, Any]) -> Dict[str, Any]:
1462
1471
  """Convert a tool to a dictionary format."""
1463
1472
  return tool
1464
1473
 
1465
-
1466
1474
  def image_bytes_to_b64_string(image_bytes: bytes, image_format: str = "jpeg") -> str:
1467
1475
  """Convert image bytes to base64 string."""
1468
1476
  import base64
1469
-
1470
1477
  return f"data:image/{image_format};base64,{base64.b64encode(image_bytes).decode('utf-8')}"