blaxel 0.2.31__py3-none-any.whl → 0.2.31rc121__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.
- blaxel/__init__.py +3 -3
- blaxel/core/agents/__init__.py +6 -13
- blaxel/core/authentication/__init__.py +1 -2
- blaxel/core/authentication/devicemode.py +1 -9
- blaxel/core/authentication/oauth.py +6 -13
- blaxel/core/authentication/types.py +0 -1
- blaxel/core/cache/cache.py +3 -10
- blaxel/core/client/api/agents/list_agent_revisions.py +1 -3
- blaxel/core/client/api/compute/delete_sandbox_preview_token.py +2 -6
- blaxel/core/client/api/compute/start_sandbox.py +1 -3
- blaxel/core/client/api/compute/stop_sandbox.py +1 -3
- blaxel/core/client/api/default/list_sandbox_hub_definitions.py +2 -6
- blaxel/core/client/api/functions/list_function_revisions.py +1 -3
- blaxel/core/client/api/images/cleanup_images.py +1 -3
- blaxel/core/client/api/integrations/list_integration_connections.py +2 -6
- blaxel/core/client/api/invitations/list_all_pending_invitations.py +1 -3
- blaxel/core/client/api/jobs/create_job_execution.py +1 -3
- blaxel/core/client/api/jobs/delete_job_execution.py +1 -3
- blaxel/core/client/api/jobs/get_job_execution.py +1 -3
- blaxel/core/client/api/jobs/list_job_executions.py +2 -6
- blaxel/core/client/api/jobs/list_job_revisions.py +1 -3
- blaxel/core/client/api/locations/list_locations.py +1 -3
- blaxel/core/client/api/models/list_model_revisions.py +1 -3
- blaxel/core/client/api/service_accounts/create_workspace_service_account.py +2 -6
- blaxel/core/client/api/service_accounts/delete_workspace_service_account.py +2 -6
- blaxel/core/client/api/service_accounts/get_workspace_service_accounts.py +1 -3
- blaxel/core/client/api/service_accounts/update_workspace_service_account.py +2 -6
- blaxel/core/client/api/volume_templates/list_volume_templates.py +1 -3
- blaxel/core/client/api/workspaces/accept_workspace_invitation.py +2 -6
- blaxel/core/client/api/workspaces/invite_workspace_user.py +2 -6
- blaxel/core/client/api/workspaces/update_workspace_user_role.py +2 -6
- blaxel/core/client/client.py +1 -3
- blaxel/core/client/models/agent.py +4 -11
- blaxel/core/client/models/agent_spec.py +5 -18
- blaxel/core/client/models/billable_time_metric.py +1 -0
- blaxel/core/client/models/configuration.py +1 -0
- blaxel/core/client/models/core_spec.py +3 -10
- blaxel/core/client/models/core_spec_configurations.py +1 -0
- blaxel/core/client/models/create_job_execution_request.py +1 -0
- blaxel/core/client/models/create_job_execution_response.py +1 -0
- blaxel/core/client/models/custom_domain.py +2 -5
- blaxel/core/client/models/custom_domain_metadata.py +1 -0
- blaxel/core/client/models/custom_domain_spec.py +2 -5
- blaxel/core/client/models/delete_volume_template_version_response_200.py +2 -5
- blaxel/core/client/models/entrypoint.py +1 -0
- blaxel/core/client/models/form.py +2 -5
- blaxel/core/client/models/function.py +4 -11
- blaxel/core/client/models/function_spec.py +4 -13
- blaxel/core/client/models/image.py +2 -5
- blaxel/core/client/models/image_spec.py +1 -0
- blaxel/core/client/models/integration.py +3 -10
- blaxel/core/client/models/integration_connection.py +2 -5
- blaxel/core/client/models/integration_connection_spec.py +1 -0
- blaxel/core/client/models/integration_endpoint.py +2 -5
- blaxel/core/client/models/integration_endpoints.py +2 -0
- blaxel/core/client/models/job.py +4 -11
- blaxel/core/client/models/job_execution.py +2 -5
- blaxel/core/client/models/job_execution_spec.py +1 -0
- blaxel/core/client/models/job_execution_task.py +2 -5
- blaxel/core/client/models/job_metrics.py +2 -5
- blaxel/core/client/models/job_spec.py +4 -13
- blaxel/core/client/models/jobs_network_chart.py +1 -0
- blaxel/core/client/models/jobs_success_failed_chart.py +3 -10
- blaxel/core/client/models/latency_metric.py +2 -5
- blaxel/core/client/models/location_response.py +1 -0
- blaxel/core/client/models/mcp_definition.py +2 -5
- blaxel/core/client/models/metadata.py +1 -0
- blaxel/core/client/models/metrics.py +4 -11
- blaxel/core/client/models/model.py +4 -11
- blaxel/core/client/models/model_spec.py +3 -10
- blaxel/core/client/models/pending_invitation_accept.py +2 -5
- blaxel/core/client/models/pending_invitation_render.py +3 -10
- blaxel/core/client/models/policy.py +2 -5
- blaxel/core/client/models/policy_spec.py +4 -11
- blaxel/core/client/models/preview.py +2 -5
- blaxel/core/client/models/preview_spec.py +1 -0
- blaxel/core/client/models/preview_token.py +2 -5
- blaxel/core/client/models/public_ips.py +1 -0
- blaxel/core/client/models/request_duration_over_time_metrics.py +1 -0
- blaxel/core/client/models/request_total_by_origin_metric.py +7 -16
- blaxel/core/client/models/request_total_metric.py +3 -8
- blaxel/core/client/models/resource_metrics.py +17 -58
- blaxel/core/client/models/runtime.py +1 -0
- blaxel/core/client/models/sandbox.py +4 -11
- blaxel/core/client/models/sandbox_definition.py +1 -0
- blaxel/core/client/models/sandbox_lifecycle.py +1 -0
- blaxel/core/client/models/sandbox_spec.py +6 -21
- blaxel/core/client/models/serverless_config.py +1 -0
- blaxel/core/client/models/start_sandbox.py +2 -5
- blaxel/core/client/models/stop_sandbox.py +2 -5
- blaxel/core/client/models/store_agent.py +1 -0
- blaxel/core/client/models/store_configuration.py +1 -0
- blaxel/core/client/models/template.py +1 -0
- blaxel/core/client/models/time_to_first_token_over_time_metrics.py +2 -3
- blaxel/core/client/models/token_rate_metrics.py +1 -0
- blaxel/core/client/models/trigger.py +1 -0
- blaxel/core/client/models/trigger_configuration.py +1 -0
- blaxel/core/client/models/volume.py +4 -11
- blaxel/core/client/models/volume_template.py +2 -5
- blaxel/core/client/models/workspace.py +2 -5
- blaxel/core/client/response_interceptor.py +1 -3
- blaxel/core/common/autoload.py +11 -9
- blaxel/core/common/env.py +8 -10
- blaxel/core/common/settings.py +2 -4
- blaxel/core/common/webhook.py +1 -0
- blaxel/core/jobs/__init__.py +3 -13
- blaxel/core/mcp/client.py +2 -8
- blaxel/core/mcp/server.py +2 -8
- blaxel/core/models/__init__.py +5 -6
- blaxel/core/sandbox/__init__.py +1 -1
- blaxel/core/sandbox/client/api/codegen/get_codegen_reranking_path.py +2 -6
- blaxel/core/sandbox/client/api/fastapply/put_codegen_fastapply_path.py +2 -6
- blaxel/core/sandbox/client/api/filesystem/delete_filesystem_multipart_upload_id_abort.py +2 -6
- blaxel/core/sandbox/client/api/filesystem/delete_filesystem_path.py +2 -6
- blaxel/core/sandbox/client/api/filesystem/delete_filesystem_tree_path.py +2 -6
- blaxel/core/sandbox/client/api/filesystem/get_filesystem_content_search_path.py +1 -3
- blaxel/core/sandbox/client/api/filesystem/get_filesystem_find_path.py +2 -6
- blaxel/core/sandbox/client/api/filesystem/get_filesystem_search_path.py +2 -6
- blaxel/core/sandbox/client/api/filesystem/get_watch_filesystem_path.py +2 -6
- blaxel/core/sandbox/client/api/filesystem/post_filesystem_multipart_upload_id_complete.py +2 -6
- blaxel/core/sandbox/client/api/filesystem/put_filesystem_path.py +2 -6
- blaxel/core/sandbox/client/api/process/delete_process_identifier.py +2 -6
- blaxel/core/sandbox/client/api/process/delete_process_identifier_kill.py +2 -6
- blaxel/core/sandbox/client/api/process/get_process.py +1 -3
- blaxel/core/sandbox/client/api/process/get_process_identifier.py +2 -6
- blaxel/core/sandbox/client/api/process/get_process_identifier_logs.py +2 -6
- blaxel/core/sandbox/client/api/process/get_process_identifier_logs_stream.py +2 -6
- blaxel/core/sandbox/client/api/process/post_process.py +2 -6
- blaxel/core/sandbox/client/client.py +1 -3
- blaxel/core/sandbox/client/models/filesystem_multipart_upload_parts.py +1 -3
- blaxel/core/sandbox/default/__init__.py +1 -0
- blaxel/core/sandbox/default/action.py +3 -3
- blaxel/core/sandbox/default/codegen.py +4 -2
- blaxel/core/sandbox/default/filesystem.py +82 -38
- blaxel/core/sandbox/default/interpreter.py +10 -17
- blaxel/core/sandbox/default/preview.py +2 -6
- blaxel/core/sandbox/default/process.py +7 -25
- blaxel/core/sandbox/default/sandbox.py +2 -7
- blaxel/core/sandbox/sync/__init__.py +2 -0
- blaxel/core/sandbox/sync/action.py +3 -2
- blaxel/core/sandbox/sync/codegen.py +5 -1
- blaxel/core/sandbox/sync/filesystem.py +6 -17
- blaxel/core/sandbox/sync/interpreter.py +6 -10
- blaxel/core/sandbox/sync/network.py +2 -0
- blaxel/core/sandbox/sync/preview.py +9 -21
- blaxel/core/sandbox/sync/process.py +8 -32
- blaxel/core/sandbox/sync/sandbox.py +6 -13
- blaxel/core/sandbox/sync/session.py +4 -6
- blaxel/core/sandbox/types.py +1 -2
- blaxel/core/tools/__init__.py +6 -30
- blaxel/core/tools/common.py +1 -1
- blaxel/core/tools/types.py +1 -2
- blaxel/crewai/model.py +5 -20
- blaxel/googleadk/__init__.py +1 -1
- blaxel/googleadk/tools.py +5 -3
- blaxel/langgraph/custom/gemini.py +133 -126
- blaxel/langgraph/model.py +50 -54
- blaxel/langgraph/tools.py +3 -9
- blaxel/llamaindex/custom/cohere.py +16 -25
- blaxel/llamaindex/model.py +57 -44
- blaxel/llamaindex/tools.py +3 -2
- blaxel/pydantic/custom/gemini.py +3 -3
- blaxel/pydantic/tools.py +4 -2
- blaxel/telemetry/exporters.py +3 -10
- blaxel/telemetry/instrumentation/blaxel_langgraph.py +2 -4
- blaxel/telemetry/instrumentation/blaxel_langgraph_gemini.py +5 -22
- blaxel/telemetry/instrumentation/utils.py +3 -3
- blaxel/telemetry/log/log.py +3 -2
- blaxel/telemetry/log/logger.py +15 -21
- blaxel/telemetry/span.py +6 -10
- {blaxel-0.2.31.dist-info → blaxel-0.2.31rc121.dist-info}/METADATA +2 -2
- {blaxel-0.2.31.dist-info → blaxel-0.2.31rc121.dist-info}/RECORD +174 -174
- {blaxel-0.2.31.dist-info → blaxel-0.2.31rc121.dist-info}/WHEEL +0 -0
- {blaxel-0.2.31.dist-info → blaxel-0.2.31rc121.dist-info}/licenses/LICENSE +0 -0
blaxel/crewai/model.py
CHANGED
|
@@ -18,24 +18,15 @@ async def bl_model(name: str, **kwargs):
|
|
|
18
18
|
url, type, model = await bl_model_core(name).get_parameters()
|
|
19
19
|
if type == "mistral":
|
|
20
20
|
return AuthenticatedLLM(
|
|
21
|
-
model=f"mistral/{model}",
|
|
22
|
-
api_key="replaced",
|
|
23
|
-
base_url=f"{url}/v1",
|
|
24
|
-
**kwargs,
|
|
21
|
+
model=f"mistral/{model}", api_key="replaced", base_url=f"{url}/v1", **kwargs
|
|
25
22
|
)
|
|
26
23
|
elif type == "xai":
|
|
27
24
|
return AuthenticatedLLM(
|
|
28
|
-
model=f"groq/{model}",
|
|
29
|
-
api_key="replaced",
|
|
30
|
-
base_url=f"{url}/v1",
|
|
31
|
-
**kwargs,
|
|
25
|
+
model=f"groq/{model}", api_key="replaced", base_url=f"{url}/v1", **kwargs
|
|
32
26
|
)
|
|
33
27
|
elif type == "deepseek":
|
|
34
28
|
return AuthenticatedLLM(
|
|
35
|
-
model=f"openai/{model}",
|
|
36
|
-
api_key="replaced",
|
|
37
|
-
base_url=f"{url}/v1",
|
|
38
|
-
**kwargs,
|
|
29
|
+
model=f"openai/{model}", api_key="replaced", base_url=f"{url}/v1", **kwargs
|
|
39
30
|
)
|
|
40
31
|
elif type == "anthropic":
|
|
41
32
|
return AuthenticatedLLM(
|
|
@@ -53,17 +44,11 @@ async def bl_model(name: str, **kwargs):
|
|
|
53
44
|
)
|
|
54
45
|
elif type == "cerebras":
|
|
55
46
|
return AuthenticatedLLM(
|
|
56
|
-
model=f"cerebras/{model}",
|
|
57
|
-
api_key="replaced",
|
|
58
|
-
base_url=f"{url}/v1",
|
|
59
|
-
**kwargs,
|
|
47
|
+
model=f"cerebras/{model}", api_key="replaced", base_url=f"{url}/v1", **kwargs
|
|
60
48
|
)
|
|
61
49
|
else:
|
|
62
50
|
if type != "openai":
|
|
63
51
|
logger.warning(f"Model {model} is not supported by CrewAI, defaulting to OpenAI")
|
|
64
52
|
return AuthenticatedLLM(
|
|
65
|
-
model=f"openai/{model}",
|
|
66
|
-
api_key="replaced",
|
|
67
|
-
base_url=f"{url}/v1",
|
|
68
|
-
**kwargs,
|
|
53
|
+
model=f"openai/{model}", api_key="replaced", base_url=f"{url}/v1", **kwargs
|
|
69
54
|
)
|
blaxel/googleadk/__init__.py
CHANGED
blaxel/googleadk/tools.py
CHANGED
|
@@ -55,11 +55,13 @@ class GoogleADKTool(BaseTool):
|
|
|
55
55
|
return function_decl
|
|
56
56
|
|
|
57
57
|
@override
|
|
58
|
-
async def run_async(
|
|
58
|
+
async def run_async(
|
|
59
|
+
self, *, args: dict[str, Any], tool_context: ToolContext
|
|
60
|
+
) -> Any:
|
|
59
61
|
args_to_call = args.copy()
|
|
60
62
|
signature = inspect.signature(self._tool.coroutine)
|
|
61
|
-
if
|
|
62
|
-
args_to_call[
|
|
63
|
+
if 'tool_context' in signature.parameters:
|
|
64
|
+
args_to_call['tool_context'] = tool_context
|
|
63
65
|
return await self._tool.coroutine(**args_to_call) or {}
|
|
64
66
|
|
|
65
67
|
|
|
@@ -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=
|
|
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(
|
|
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(
|
|
242
|
-
kwargs.pop(
|
|
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 =
|
|
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(
|
|
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(
|
|
287
|
-
kwargs.pop(
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 = [
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
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(
|
|
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(
|
|
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] =
|
|
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(
|
|
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] =
|
|
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(
|
|
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 = [
|
|
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)
|
|
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(
|
|
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(
|
|
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[
|
|
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 = [
|
|
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(
|
|
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
|
-
|
|
1398
|
-
|
|
1399
|
-
"
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
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
|
-
|
|
1411
|
-
|
|
1412
|
-
"
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
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
|
-
|
|
1430
|
-
|
|
1431
|
-
"
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
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
|
-
|
|
1442
|
-
|
|
1443
|
-
"
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
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')}"
|