langchain-google-genai 1.0.2__tar.gz → 1.0.3__tar.gz
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.
Potentially problematic release.
This version of langchain-google-genai might be problematic. Click here for more details.
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/PKG-INFO +3 -3
- langchain_google_genai-1.0.3/langchain_google_genai/_function_utils.py +241 -0
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/chat_models.py +185 -25
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/embeddings.py +6 -0
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/llms.py +8 -0
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/pyproject.toml +4 -4
- langchain_google_genai-1.0.2/langchain_google_genai/_function_utils.py +0 -116
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/LICENSE +0 -0
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/README.md +0 -0
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/__init__.py +0 -0
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/_common.py +0 -0
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/_enums.py +0 -0
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/_genai_extension.py +0 -0
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/genai_aqa.py +0 -0
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/google_vector_store.py +0 -0
- {langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: langchain-google-genai
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: An integration package connecting Google's genai package and LangChain
|
|
5
5
|
Home-page: https://github.com/langchain-ai/langchain-google
|
|
6
6
|
License: MIT
|
|
@@ -12,8 +12,8 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Provides-Extra: images
|
|
15
|
-
Requires-Dist: google-generativeai (>=0.5.
|
|
16
|
-
Requires-Dist: langchain-core (>=0.1.
|
|
15
|
+
Requires-Dist: google-generativeai (>=0.5.2,<0.6.0)
|
|
16
|
+
Requires-Dist: langchain-core (>=0.1.45,<0.2)
|
|
17
17
|
Requires-Dist: pillow (>=10.1.0,<11.0.0) ; extra == "images"
|
|
18
18
|
Project-URL: Repository, https://github.com/langchain-ai/langchain-google
|
|
19
19
|
Project-URL: Source Code, https://github.com/langchain-ai/langchain-google/tree/main/libs/genai
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import (
|
|
4
|
+
Any,
|
|
5
|
+
Dict,
|
|
6
|
+
List,
|
|
7
|
+
Literal,
|
|
8
|
+
Optional,
|
|
9
|
+
Sequence,
|
|
10
|
+
Type,
|
|
11
|
+
TypedDict,
|
|
12
|
+
Union,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
import google.ai.generativelanguage as glm
|
|
16
|
+
from google.generativeai.types import Tool as GoogleTool # type: ignore[import]
|
|
17
|
+
from google.generativeai.types.content_types import ( # type: ignore[import]
|
|
18
|
+
FunctionCallingConfigType,
|
|
19
|
+
FunctionDeclarationType,
|
|
20
|
+
ToolDict,
|
|
21
|
+
ToolType,
|
|
22
|
+
)
|
|
23
|
+
from langchain_core.pydantic_v1 import BaseModel
|
|
24
|
+
from langchain_core.tools import BaseTool
|
|
25
|
+
from langchain_core.tools import tool as callable_as_lc_tool
|
|
26
|
+
from langchain_core.utils.json_schema import dereference_refs
|
|
27
|
+
|
|
28
|
+
TYPE_ENUM = {
|
|
29
|
+
"string": glm.Type.STRING,
|
|
30
|
+
"number": glm.Type.NUMBER,
|
|
31
|
+
"integer": glm.Type.INTEGER,
|
|
32
|
+
"boolean": glm.Type.BOOLEAN,
|
|
33
|
+
"array": glm.Type.ARRAY,
|
|
34
|
+
"object": glm.Type.OBJECT,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
TYPE_ENUM_REVERSE = {v: k for k, v in TYPE_ENUM.items()}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def convert_to_genai_function_declarations(
|
|
41
|
+
tool: Union[
|
|
42
|
+
GoogleTool, ToolDict, FunctionDeclarationType, Sequence[FunctionDeclarationType]
|
|
43
|
+
],
|
|
44
|
+
) -> ToolType:
|
|
45
|
+
"""Convert any tool-like object to a ToolType.
|
|
46
|
+
|
|
47
|
+
https://github.com/google-gemini/generative-ai-python/blob/668695ebe3e9de496a36eeb95cb2ed2faba9b939/google/generativeai/types/content_types.py#L574
|
|
48
|
+
"""
|
|
49
|
+
if isinstance(tool, GoogleTool):
|
|
50
|
+
return tool
|
|
51
|
+
# check whether a dict is supported by glm, otherwise we parse it explicitly
|
|
52
|
+
if isinstance(tool, dict):
|
|
53
|
+
first_function_declaration = tool.get("function_declarations", [None])[0]
|
|
54
|
+
if isinstance(first_function_declaration, glm.FunctionDeclaration):
|
|
55
|
+
return tool
|
|
56
|
+
schema = None
|
|
57
|
+
try:
|
|
58
|
+
schema = first_function_declaration.parameters
|
|
59
|
+
except AttributeError:
|
|
60
|
+
pass
|
|
61
|
+
if schema is None:
|
|
62
|
+
schema = first_function_declaration.get("parameters")
|
|
63
|
+
if schema is None or isinstance(schema, glm.Schema):
|
|
64
|
+
return tool
|
|
65
|
+
return glm.Tool(
|
|
66
|
+
function_declarations=[
|
|
67
|
+
_convert_to_genai_function(fc) for fc in tool["function_declarations"]
|
|
68
|
+
],
|
|
69
|
+
)
|
|
70
|
+
elif isinstance(tool, type) and issubclass(tool, BaseModel):
|
|
71
|
+
return glm.Tool(function_declarations=[_convert_to_genai_function(tool)])
|
|
72
|
+
elif callable(tool):
|
|
73
|
+
return _convert_tool_to_genai_function(callable_as_lc_tool()(tool))
|
|
74
|
+
elif isinstance(tool, list):
|
|
75
|
+
return glm.Tool(
|
|
76
|
+
function_declarations=[_convert_to_genai_function(fc) for fc in tool]
|
|
77
|
+
)
|
|
78
|
+
return glm.Tool(function_declarations=[_convert_to_genai_function(tool)])
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def tool_to_dict(tool: Union[glm.Tool, GoogleTool]) -> ToolDict:
|
|
82
|
+
if isinstance(tool, GoogleTool):
|
|
83
|
+
tool = tool._proto
|
|
84
|
+
function_declarations = []
|
|
85
|
+
for function_declaration_proto in tool.function_declarations:
|
|
86
|
+
properties: Dict[str, Any] = {}
|
|
87
|
+
for property in function_declaration_proto.parameters.properties:
|
|
88
|
+
property_type = function_declaration_proto.parameters.properties[
|
|
89
|
+
property
|
|
90
|
+
].type
|
|
91
|
+
property_dict = {"type": TYPE_ENUM_REVERSE[property_type]}
|
|
92
|
+
property_description = function_declaration_proto.parameters.properties[
|
|
93
|
+
property
|
|
94
|
+
].description
|
|
95
|
+
if property_description:
|
|
96
|
+
property_dict["description"] = property_description
|
|
97
|
+
properties[property] = property_dict
|
|
98
|
+
function_declaration = {
|
|
99
|
+
"name": function_declaration_proto.name,
|
|
100
|
+
"description": function_declaration_proto.description,
|
|
101
|
+
"parameters": {"type": "object", "properties": properties},
|
|
102
|
+
}
|
|
103
|
+
if function_declaration_proto.parameters.required:
|
|
104
|
+
function_declaration["parameters"][ # type: ignore[index]
|
|
105
|
+
"required"
|
|
106
|
+
] = function_declaration_proto.parameters.required
|
|
107
|
+
function_declarations.append(function_declaration)
|
|
108
|
+
return {"function_declarations": function_declarations}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _convert_to_genai_function(fc: FunctionDeclarationType) -> glm.FunctionDeclaration:
|
|
112
|
+
if isinstance(fc, BaseTool):
|
|
113
|
+
return _convert_tool_to_genai_function(fc)
|
|
114
|
+
elif isinstance(fc, type) and issubclass(fc, BaseModel):
|
|
115
|
+
return _convert_pydantic_to_genai_function(fc)
|
|
116
|
+
elif callable(fc):
|
|
117
|
+
return _convert_tool_to_genai_function(callable_as_lc_tool()(fc))
|
|
118
|
+
elif isinstance(fc, dict):
|
|
119
|
+
return glm.FunctionDeclaration(
|
|
120
|
+
name=fc["name"],
|
|
121
|
+
description=fc.get("description"),
|
|
122
|
+
parameters={
|
|
123
|
+
"properties": {
|
|
124
|
+
k: {
|
|
125
|
+
"type_": TYPE_ENUM[v["type"]],
|
|
126
|
+
"description": v.get("description"),
|
|
127
|
+
}
|
|
128
|
+
for k, v in fc["parameters"]["properties"].items()
|
|
129
|
+
},
|
|
130
|
+
"required": fc["parameters"].get("required", []),
|
|
131
|
+
"type_": TYPE_ENUM[fc["parameters"]["type"]],
|
|
132
|
+
},
|
|
133
|
+
)
|
|
134
|
+
else:
|
|
135
|
+
raise ValueError(f"Unsupported function call type {fc}")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _convert_tool_to_genai_function(tool: BaseTool) -> glm.FunctionDeclaration:
|
|
139
|
+
if tool.args_schema:
|
|
140
|
+
schema = dereference_refs(tool.args_schema.schema())
|
|
141
|
+
schema.pop("definitions", None)
|
|
142
|
+
return glm.FunctionDeclaration(
|
|
143
|
+
name=tool.name or schema["title"],
|
|
144
|
+
description=tool.description or schema["description"],
|
|
145
|
+
parameters={
|
|
146
|
+
"properties": {
|
|
147
|
+
k: {
|
|
148
|
+
"type_": TYPE_ENUM[v["type"]],
|
|
149
|
+
"description": v.get("description"),
|
|
150
|
+
}
|
|
151
|
+
for k, v in schema["properties"].items()
|
|
152
|
+
},
|
|
153
|
+
"required": schema.get("required", []),
|
|
154
|
+
"type_": TYPE_ENUM[schema["type"]],
|
|
155
|
+
},
|
|
156
|
+
)
|
|
157
|
+
else:
|
|
158
|
+
return glm.FunctionDeclaration(
|
|
159
|
+
name=tool.name,
|
|
160
|
+
description=tool.description,
|
|
161
|
+
parameters={
|
|
162
|
+
"properties": {
|
|
163
|
+
"__arg1": {"type_": TYPE_ENUM["string"]},
|
|
164
|
+
},
|
|
165
|
+
"required": ["__arg1"],
|
|
166
|
+
"type_": TYPE_ENUM["object"],
|
|
167
|
+
},
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _convert_pydantic_to_genai_function(
|
|
172
|
+
pydantic_model: Type[BaseModel],
|
|
173
|
+
) -> glm.FunctionDeclaration:
|
|
174
|
+
schema = dereference_refs(pydantic_model.schema())
|
|
175
|
+
schema.pop("definitions", None)
|
|
176
|
+
return glm.FunctionDeclaration(
|
|
177
|
+
name=schema["title"],
|
|
178
|
+
description=schema.get("description", ""),
|
|
179
|
+
parameters={
|
|
180
|
+
"properties": {
|
|
181
|
+
k: {
|
|
182
|
+
"type_": TYPE_ENUM[v["type"]],
|
|
183
|
+
"description": v.get("description"),
|
|
184
|
+
}
|
|
185
|
+
for k, v in schema["properties"].items()
|
|
186
|
+
},
|
|
187
|
+
"required": schema["required"],
|
|
188
|
+
"type_": TYPE_ENUM[schema["type"]],
|
|
189
|
+
},
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
_ToolChoiceType = Union[
|
|
194
|
+
dict, List[str], str, Literal["auto", "none", "any"], Literal[True]
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class _ToolConfigDict(TypedDict):
|
|
199
|
+
function_calling_config: FunctionCallingConfigType
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _tool_choice_to_tool_config(
|
|
203
|
+
tool_choice: _ToolChoiceType,
|
|
204
|
+
all_names: List[str],
|
|
205
|
+
) -> _ToolConfigDict:
|
|
206
|
+
allowed_function_names: Optional[List[str]] = None
|
|
207
|
+
if tool_choice is True or tool_choice == "any":
|
|
208
|
+
mode = "any"
|
|
209
|
+
allowed_function_names = all_names
|
|
210
|
+
elif tool_choice == "auto":
|
|
211
|
+
mode = "auto"
|
|
212
|
+
elif tool_choice == "none":
|
|
213
|
+
mode = "none"
|
|
214
|
+
elif isinstance(tool_choice, str):
|
|
215
|
+
mode = "any"
|
|
216
|
+
allowed_function_names = [tool_choice]
|
|
217
|
+
elif isinstance(tool_choice, list):
|
|
218
|
+
mode = "any"
|
|
219
|
+
allowed_function_names = tool_choice
|
|
220
|
+
elif isinstance(tool_choice, dict):
|
|
221
|
+
if "mode" in tool_choice:
|
|
222
|
+
mode = tool_choice["mode"]
|
|
223
|
+
allowed_function_names = tool_choice.get("allowed_function_names")
|
|
224
|
+
elif "function_calling_config" in tool_choice:
|
|
225
|
+
mode = tool_choice["function_calling_config"]["mode"]
|
|
226
|
+
allowed_function_names = tool_choice["function_calling_config"].get(
|
|
227
|
+
"allowed_function_names"
|
|
228
|
+
)
|
|
229
|
+
else:
|
|
230
|
+
raise ValueError(
|
|
231
|
+
f"Unrecognized tool choice format:\n\n{tool_choice=}\n\nShould match "
|
|
232
|
+
f"Google GenerativeAI ToolConfig or FunctionCallingConfig format."
|
|
233
|
+
)
|
|
234
|
+
else:
|
|
235
|
+
raise ValueError(f"Unrecognized tool choice format:\n\n{tool_choice=}")
|
|
236
|
+
return _ToolConfigDict(
|
|
237
|
+
function_calling_config={
|
|
238
|
+
"mode": mode,
|
|
239
|
+
"allowed_function_names": allowed_function_names,
|
|
240
|
+
}
|
|
241
|
+
)
|
{langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/chat_models.py
RENAMED
|
@@ -4,6 +4,7 @@ import base64
|
|
|
4
4
|
import json
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
|
+
import uuid
|
|
7
8
|
import warnings
|
|
8
9
|
from io import BytesIO
|
|
9
10
|
from typing import (
|
|
@@ -29,10 +30,17 @@ import google.api_core
|
|
|
29
30
|
import google.generativeai as genai # type: ignore[import]
|
|
30
31
|
import proto # type: ignore[import]
|
|
31
32
|
import requests
|
|
33
|
+
from google.generativeai.types import SafetySettingDict # type: ignore[import]
|
|
34
|
+
from google.generativeai.types import Tool as GoogleTool # type: ignore[import]
|
|
35
|
+
from google.generativeai.types.content_types import ( # type: ignore[import]
|
|
36
|
+
FunctionDeclarationType,
|
|
37
|
+
ToolDict,
|
|
38
|
+
)
|
|
32
39
|
from langchain_core.callbacks.manager import (
|
|
33
40
|
AsyncCallbackManagerForLLMRun,
|
|
34
41
|
CallbackManagerForLLMRun,
|
|
35
42
|
)
|
|
43
|
+
from langchain_core.language_models import LanguageModelInput
|
|
36
44
|
from langchain_core.language_models.chat_models import BaseChatModel
|
|
37
45
|
from langchain_core.messages import (
|
|
38
46
|
AIMessage,
|
|
@@ -40,10 +48,16 @@ from langchain_core.messages import (
|
|
|
40
48
|
BaseMessage,
|
|
41
49
|
FunctionMessage,
|
|
42
50
|
HumanMessage,
|
|
51
|
+
InvalidToolCall,
|
|
43
52
|
SystemMessage,
|
|
53
|
+
ToolCall,
|
|
54
|
+
ToolCallChunk,
|
|
55
|
+
ToolMessage,
|
|
44
56
|
)
|
|
57
|
+
from langchain_core.output_parsers.openai_tools import parse_tool_calls
|
|
45
58
|
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
|
|
46
59
|
from langchain_core.pydantic_v1 import SecretStr, root_validator
|
|
60
|
+
from langchain_core.runnables import Runnable
|
|
47
61
|
from langchain_core.utils import get_from_dict_or_env
|
|
48
62
|
from tenacity import (
|
|
49
63
|
before_sleep_log,
|
|
@@ -55,7 +69,11 @@ from tenacity import (
|
|
|
55
69
|
|
|
56
70
|
from langchain_google_genai._common import GoogleGenerativeAIError
|
|
57
71
|
from langchain_google_genai._function_utils import (
|
|
72
|
+
_tool_choice_to_tool_config,
|
|
73
|
+
_ToolChoiceType,
|
|
74
|
+
_ToolConfigDict,
|
|
58
75
|
convert_to_genai_function_declarations,
|
|
76
|
+
tool_to_dict,
|
|
59
77
|
)
|
|
60
78
|
from langchain_google_genai.llms import GoogleModelFamily, _BaseGoogleGenerativeAI
|
|
61
79
|
|
|
@@ -350,6 +368,40 @@ def _parse_chat_history(
|
|
|
350
368
|
)
|
|
351
369
|
)
|
|
352
370
|
]
|
|
371
|
+
elif isinstance(message, ToolMessage):
|
|
372
|
+
role = "user"
|
|
373
|
+
prev_message: Optional[BaseMessage] = (
|
|
374
|
+
input_messages[i - 1] if i > 0 else None
|
|
375
|
+
)
|
|
376
|
+
if (
|
|
377
|
+
prev_message
|
|
378
|
+
and isinstance(prev_message, AIMessage)
|
|
379
|
+
and prev_message.tool_calls
|
|
380
|
+
):
|
|
381
|
+
# message.name can be null for ToolMessage
|
|
382
|
+
name: str = prev_message.tool_calls[0]["name"]
|
|
383
|
+
else:
|
|
384
|
+
name = message.name # type: ignore
|
|
385
|
+
tool_response: Any
|
|
386
|
+
if not isinstance(message.content, str):
|
|
387
|
+
tool_response = message.content
|
|
388
|
+
else:
|
|
389
|
+
try:
|
|
390
|
+
tool_response = json.loads(message.content)
|
|
391
|
+
except json.JSONDecodeError:
|
|
392
|
+
tool_response = message.content # leave as str representation
|
|
393
|
+
parts = [
|
|
394
|
+
glm.Part(
|
|
395
|
+
function_response=glm.FunctionResponse(
|
|
396
|
+
name=name,
|
|
397
|
+
response=(
|
|
398
|
+
{"output": tool_response}
|
|
399
|
+
if not isinstance(tool_response, dict)
|
|
400
|
+
else tool_response
|
|
401
|
+
),
|
|
402
|
+
)
|
|
403
|
+
)
|
|
404
|
+
]
|
|
353
405
|
else:
|
|
354
406
|
raise ValueError(
|
|
355
407
|
f"Unexpected message with type {type(message)} at the position {i}."
|
|
@@ -360,24 +412,89 @@ def _parse_chat_history(
|
|
|
360
412
|
|
|
361
413
|
|
|
362
414
|
def _parse_response_candidate(
|
|
363
|
-
response_candidate: glm.Candidate,
|
|
415
|
+
response_candidate: glm.Candidate, streaming: bool = False
|
|
364
416
|
) -> AIMessage:
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
417
|
+
content: Union[None, str, List[str]] = None
|
|
418
|
+
additional_kwargs = {}
|
|
419
|
+
tool_calls = []
|
|
420
|
+
invalid_tool_calls = []
|
|
421
|
+
tool_call_chunks = []
|
|
422
|
+
|
|
423
|
+
for part in response_candidate.content.parts:
|
|
424
|
+
try:
|
|
425
|
+
text: Optional[str] = part.text
|
|
426
|
+
except AttributeError:
|
|
427
|
+
text = None
|
|
428
|
+
|
|
429
|
+
if text is not None:
|
|
430
|
+
if not content:
|
|
431
|
+
content = text
|
|
432
|
+
elif isinstance(content, str) and text:
|
|
433
|
+
content = [content, text]
|
|
434
|
+
elif isinstance(content, list) and text:
|
|
435
|
+
content.append(text)
|
|
436
|
+
elif text:
|
|
437
|
+
raise Exception("Unexpected content type")
|
|
438
|
+
|
|
439
|
+
if part.function_call:
|
|
440
|
+
# TODO: support multiple function calls
|
|
441
|
+
if "function_call" in additional_kwargs:
|
|
442
|
+
raise Exception("Multiple function calls are not currently supported")
|
|
443
|
+
function_call = {"name": part.function_call.name}
|
|
444
|
+
# dump to match other function calling llm for now
|
|
445
|
+
function_call_args_dict = proto.Message.to_dict(part.function_call)["args"]
|
|
446
|
+
function_call["arguments"] = json.dumps(
|
|
447
|
+
{k: function_call_args_dict[k] for k in function_call_args_dict}
|
|
448
|
+
)
|
|
449
|
+
additional_kwargs["function_call"] = function_call
|
|
450
|
+
|
|
451
|
+
if streaming:
|
|
452
|
+
tool_call_chunks.append(
|
|
453
|
+
ToolCallChunk(
|
|
454
|
+
name=function_call.get("name"),
|
|
455
|
+
args=function_call.get("arguments"),
|
|
456
|
+
id=function_call.get("id", str(uuid.uuid4())),
|
|
457
|
+
index=function_call.get("index"), # type: ignore
|
|
458
|
+
)
|
|
459
|
+
)
|
|
460
|
+
else:
|
|
461
|
+
try:
|
|
462
|
+
tool_calls_dicts = parse_tool_calls(
|
|
463
|
+
[{"function": function_call}],
|
|
464
|
+
return_id=False,
|
|
465
|
+
)
|
|
466
|
+
tool_calls = [
|
|
467
|
+
ToolCall(
|
|
468
|
+
name=tool_call["name"],
|
|
469
|
+
args=tool_call["args"],
|
|
470
|
+
id=tool_call.get("id", str(uuid.uuid4())),
|
|
471
|
+
)
|
|
472
|
+
for tool_call in tool_calls_dicts
|
|
473
|
+
]
|
|
474
|
+
except Exception as e:
|
|
475
|
+
invalid_tool_calls = [
|
|
476
|
+
InvalidToolCall(
|
|
477
|
+
name=function_call.get("name"),
|
|
478
|
+
args=function_call.get("arguments"),
|
|
479
|
+
id=function_call.get("id", str(uuid.uuid4())),
|
|
480
|
+
error=str(e),
|
|
481
|
+
)
|
|
482
|
+
]
|
|
483
|
+
if content is None:
|
|
484
|
+
content = ""
|
|
485
|
+
|
|
486
|
+
if streaming:
|
|
487
|
+
return AIMessageChunk(
|
|
488
|
+
content=cast(Union[str, List[Union[str, Dict[Any, Any]]]], content),
|
|
489
|
+
additional_kwargs=additional_kwargs,
|
|
490
|
+
tool_call_chunks=tool_call_chunks,
|
|
371
491
|
)
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
content = [proto.Message.to_dict(part) for part in parts]
|
|
379
|
-
return (AIMessageChunk if stream else AIMessage)(
|
|
380
|
-
content=content, additional_kwargs={}
|
|
492
|
+
|
|
493
|
+
return AIMessage(
|
|
494
|
+
content=cast(Union[str, List[Union[str, Dict[Any, Any]]]], content),
|
|
495
|
+
additional_kwargs=additional_kwargs,
|
|
496
|
+
tool_calls=tool_calls,
|
|
497
|
+
invalid_tool_calls=invalid_tool_calls,
|
|
381
498
|
)
|
|
382
499
|
|
|
383
500
|
|
|
@@ -400,7 +517,7 @@ def _response_to_result(
|
|
|
400
517
|
]
|
|
401
518
|
generations.append(
|
|
402
519
|
(ChatGenerationChunk if stream else ChatGeneration)(
|
|
403
|
-
message=_parse_response_candidate(candidate,
|
|
520
|
+
message=_parse_response_candidate(candidate, streaming=stream),
|
|
404
521
|
generation_info=generation_info,
|
|
405
522
|
)
|
|
406
523
|
)
|
|
@@ -627,20 +744,29 @@ class ChatGoogleGenerativeAI(_BaseGoogleGenerativeAI, BaseChatModel):
|
|
|
627
744
|
self,
|
|
628
745
|
messages: List[BaseMessage],
|
|
629
746
|
stop: Optional[List[str]] = None,
|
|
747
|
+
tools: Optional[Sequence[Union[ToolDict, GoogleTool]]] = None,
|
|
748
|
+
functions: Optional[Sequence[FunctionDeclarationType]] = None,
|
|
749
|
+
safety_settings: Optional[SafetySettingDict] = None,
|
|
750
|
+
tool_config: Optional[Union[Dict, _ToolConfigDict]] = None,
|
|
630
751
|
**kwargs: Any,
|
|
631
752
|
) -> Tuple[Dict[str, Any], genai.ChatSession, genai.types.ContentDict]:
|
|
632
753
|
client = self.client
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
754
|
+
formatted_tools = None
|
|
755
|
+
if tools:
|
|
756
|
+
formatted_tools = [
|
|
757
|
+
convert_to_genai_function_declarations(tool) for tool in tools
|
|
758
|
+
]
|
|
759
|
+
elif functions:
|
|
760
|
+
formatted_tools = [convert_to_genai_function_declarations(functions)]
|
|
761
|
+
|
|
762
|
+
if formatted_tools or safety_settings:
|
|
639
763
|
client = genai.GenerativeModel(
|
|
640
|
-
model_name=self.model,
|
|
764
|
+
model_name=self.model,
|
|
765
|
+
tools=formatted_tools,
|
|
766
|
+
safety_settings=safety_settings,
|
|
641
767
|
)
|
|
642
768
|
|
|
643
|
-
params = self._prepare_params(stop, **kwargs)
|
|
769
|
+
params = self._prepare_params(stop, tool_config=tool_config, **kwargs)
|
|
644
770
|
system_instruction, history = _parse_chat_history(
|
|
645
771
|
messages,
|
|
646
772
|
convert_system_message_to_human=self.convert_system_message_to_human,
|
|
@@ -672,3 +798,37 @@ class ChatGoogleGenerativeAI(_BaseGoogleGenerativeAI, BaseChatModel):
|
|
|
672
798
|
token_count = result["token_count"]
|
|
673
799
|
|
|
674
800
|
return token_count
|
|
801
|
+
|
|
802
|
+
def bind_tools(
|
|
803
|
+
self,
|
|
804
|
+
tools: Sequence[Union[ToolDict, GoogleTool]],
|
|
805
|
+
tool_config: Optional[Union[Dict, _ToolConfigDict]] = None,
|
|
806
|
+
*,
|
|
807
|
+
tool_choice: Optional[Union[_ToolChoiceType, bool]] = None,
|
|
808
|
+
**kwargs: Any,
|
|
809
|
+
) -> Runnable[LanguageModelInput, BaseMessage]:
|
|
810
|
+
"""Bind tool-like objects to this chat model.
|
|
811
|
+
|
|
812
|
+
Assumes model is compatible with google-generativeAI tool-calling API.
|
|
813
|
+
|
|
814
|
+
Args:
|
|
815
|
+
tools: A list of tool definitions to bind to this chat model.
|
|
816
|
+
Can be a pydantic model, callable, or BaseTool. Pydantic
|
|
817
|
+
models, callables, and BaseTools will be automatically converted to
|
|
818
|
+
their schema dictionary representation.
|
|
819
|
+
**kwargs: Any additional parameters to pass to the
|
|
820
|
+
:class:`~langchain.runnable.Runnable` constructor.
|
|
821
|
+
"""
|
|
822
|
+
if tool_choice and tool_config:
|
|
823
|
+
raise ValueError(
|
|
824
|
+
"Must specify at most one of tool_choice and tool_config, received "
|
|
825
|
+
f"both:\n\n{tool_choice=}\n\n{tool_config=}"
|
|
826
|
+
)
|
|
827
|
+
# Bind dicts for easier serialization/deserialization.
|
|
828
|
+
genai_tools = [tool_to_dict(convert_to_genai_function_declarations(tools))]
|
|
829
|
+
if tool_choice:
|
|
830
|
+
all_names = [
|
|
831
|
+
f["name"] for t in genai_tools for f in t["function_declarations"]
|
|
832
|
+
]
|
|
833
|
+
tool_config = _tool_choice_to_tool_config(tool_choice, all_names)
|
|
834
|
+
return self.bind(tools=genai_tools, tool_config=tool_config, **kwargs)
|
{langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/embeddings.py
RENAMED
|
@@ -61,6 +61,11 @@ class GoogleGenerativeAIEmbeddings(BaseModel, Embeddings):
|
|
|
61
61
|
None,
|
|
62
62
|
description="A string, one of: [`rest`, `grpc`, `grpc_asyncio`].",
|
|
63
63
|
)
|
|
64
|
+
request_options: Optional[Dict] = Field(
|
|
65
|
+
None,
|
|
66
|
+
description="A dictionary of request options to pass to the Google API client."
|
|
67
|
+
"Example: `{'timeout': 10}`",
|
|
68
|
+
)
|
|
64
69
|
|
|
65
70
|
@root_validator()
|
|
66
71
|
def validate_environment(cls, values: Dict) -> Dict:
|
|
@@ -95,6 +100,7 @@ class GoogleGenerativeAIEmbeddings(BaseModel, Embeddings):
|
|
|
95
100
|
content=texts,
|
|
96
101
|
task_type=task_type,
|
|
97
102
|
title=title,
|
|
103
|
+
request_options=self.request_options,
|
|
98
104
|
)
|
|
99
105
|
except Exception as e:
|
|
100
106
|
raise GoogleGenerativeAIError(f"Error embedding content: {e}") from e
|
{langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/llms.py
RENAMED
|
@@ -86,6 +86,7 @@ def _completion_with_retry(
|
|
|
86
86
|
stream=stream,
|
|
87
87
|
generation_config=generation_config,
|
|
88
88
|
safety_settings=kwargs.pop("safety_settings", None),
|
|
89
|
+
request_options={"timeout": llm.timeout} if llm.timeout else None,
|
|
89
90
|
)
|
|
90
91
|
return llm.client.generate_text(prompt=prompt, **kwargs)
|
|
91
92
|
except google.api_core.exceptions.FailedPrecondition as exc:
|
|
@@ -143,6 +144,10 @@ Supported examples:
|
|
|
143
144
|
not return the full n completions if duplicates are generated."""
|
|
144
145
|
max_retries: int = 6
|
|
145
146
|
"""The maximum number of retries to make when generating."""
|
|
147
|
+
|
|
148
|
+
timeout: Optional[float] = None
|
|
149
|
+
"""The maximum number of seconds to wait for a response."""
|
|
150
|
+
|
|
146
151
|
client_options: Optional[Dict] = Field(
|
|
147
152
|
None,
|
|
148
153
|
description=(
|
|
@@ -259,6 +264,9 @@ class GoogleGenerativeAI(_BaseGoogleGenerativeAI, BaseLLM):
|
|
|
259
264
|
if values["max_output_tokens"] is not None and values["max_output_tokens"] <= 0:
|
|
260
265
|
raise ValueError("max_output_tokens must be greater than zero")
|
|
261
266
|
|
|
267
|
+
if values["timeout"] is not None and values["timeout"] <= 0:
|
|
268
|
+
raise ValueError("timeout must be greater than zero")
|
|
269
|
+
|
|
262
270
|
return values
|
|
263
271
|
|
|
264
272
|
def _generate(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "langchain-google-genai"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.3"
|
|
4
4
|
description = "An integration package connecting Google's genai package and LangChain"
|
|
5
5
|
authors = []
|
|
6
6
|
readme = "README.md"
|
|
@@ -12,8 +12,8 @@ license = "MIT"
|
|
|
12
12
|
|
|
13
13
|
[tool.poetry.dependencies]
|
|
14
14
|
python = ">=3.9,<4.0"
|
|
15
|
-
langchain-core = ">=0.1.
|
|
16
|
-
google-generativeai = "^0.5.
|
|
15
|
+
langchain-core = ">=0.1.45,<0.2"
|
|
16
|
+
google-generativeai = "^0.5.2"
|
|
17
17
|
pillow = { version = "^10.1.0", optional = true }
|
|
18
18
|
|
|
19
19
|
[tool.poetry.extras]
|
|
@@ -96,7 +96,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
96
96
|
#
|
|
97
97
|
# https://github.com/tophat/syrupy
|
|
98
98
|
# --snapshot-warn-unused Prints a warning on unused snapshots rather than fail the test suite.
|
|
99
|
-
addopts = "--snapshot-warn-unused --strict-markers --strict-config --durations=5"
|
|
99
|
+
#addopts = "--snapshot-warn-unused --strict-markers --strict-config --durations=5"
|
|
100
100
|
# Registering custom markers.
|
|
101
101
|
# https://docs.pytest.org/en/7.1.x/example/markers.html#registering-markers
|
|
102
102
|
markers = [
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import (
|
|
4
|
-
Dict,
|
|
5
|
-
List,
|
|
6
|
-
Type,
|
|
7
|
-
Union,
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
import google.ai.generativelanguage as glm
|
|
11
|
-
from langchain_core.pydantic_v1 import BaseModel
|
|
12
|
-
from langchain_core.tools import BaseTool
|
|
13
|
-
from langchain_core.utils.json_schema import dereference_refs
|
|
14
|
-
|
|
15
|
-
FunctionCallType = Union[BaseTool, Type[BaseModel], Dict]
|
|
16
|
-
|
|
17
|
-
TYPE_ENUM = {
|
|
18
|
-
"string": glm.Type.STRING,
|
|
19
|
-
"number": glm.Type.NUMBER,
|
|
20
|
-
"integer": glm.Type.INTEGER,
|
|
21
|
-
"boolean": glm.Type.BOOLEAN,
|
|
22
|
-
"array": glm.Type.ARRAY,
|
|
23
|
-
"object": glm.Type.OBJECT,
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def convert_to_genai_function_declarations(
|
|
28
|
-
function_calls: List[FunctionCallType],
|
|
29
|
-
) -> List[glm.Tool]:
|
|
30
|
-
return [
|
|
31
|
-
glm.Tool(
|
|
32
|
-
function_declarations=[_convert_to_genai_function(fc)],
|
|
33
|
-
)
|
|
34
|
-
for fc in function_calls
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def _convert_to_genai_function(fc: FunctionCallType) -> glm.FunctionDeclaration:
|
|
39
|
-
if isinstance(fc, BaseTool):
|
|
40
|
-
return _convert_tool_to_genai_function(fc)
|
|
41
|
-
elif isinstance(fc, type) and issubclass(fc, BaseModel):
|
|
42
|
-
return _convert_pydantic_to_genai_function(fc)
|
|
43
|
-
elif isinstance(fc, dict):
|
|
44
|
-
return glm.FunctionDeclaration(
|
|
45
|
-
name=fc["name"],
|
|
46
|
-
description=fc.get("description"),
|
|
47
|
-
parameters={
|
|
48
|
-
"properties": {
|
|
49
|
-
k: {
|
|
50
|
-
"type_": TYPE_ENUM[v["type"]],
|
|
51
|
-
"description": v.get("description"),
|
|
52
|
-
}
|
|
53
|
-
for k, v in fc["parameters"]["properties"].items()
|
|
54
|
-
},
|
|
55
|
-
"required": fc["parameters"].get("required", []),
|
|
56
|
-
"type_": TYPE_ENUM[fc["parameters"]["type"]],
|
|
57
|
-
},
|
|
58
|
-
)
|
|
59
|
-
else:
|
|
60
|
-
raise ValueError(f"Unsupported function call type {fc}")
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def _convert_tool_to_genai_function(tool: BaseTool) -> glm.FunctionDeclaration:
|
|
64
|
-
if tool.args_schema:
|
|
65
|
-
schema = dereference_refs(tool.args_schema.schema())
|
|
66
|
-
schema.pop("definitions", None)
|
|
67
|
-
|
|
68
|
-
return glm.FunctionDeclaration(
|
|
69
|
-
name=tool.name or schema["title"],
|
|
70
|
-
description=tool.description or schema["description"],
|
|
71
|
-
parameters={
|
|
72
|
-
"properties": {
|
|
73
|
-
k: {
|
|
74
|
-
"type_": TYPE_ENUM[v["type"]],
|
|
75
|
-
"description": v.get("description"),
|
|
76
|
-
}
|
|
77
|
-
for k, v in schema["properties"].items()
|
|
78
|
-
},
|
|
79
|
-
"required": schema["required"],
|
|
80
|
-
"type_": TYPE_ENUM[schema["type"]],
|
|
81
|
-
},
|
|
82
|
-
)
|
|
83
|
-
else:
|
|
84
|
-
return glm.FunctionDeclaration(
|
|
85
|
-
name=tool.name,
|
|
86
|
-
description=tool.description,
|
|
87
|
-
parameters={
|
|
88
|
-
"properties": {
|
|
89
|
-
"__arg1": {"type_": TYPE_ENUM["string"]},
|
|
90
|
-
},
|
|
91
|
-
"required": ["__arg1"],
|
|
92
|
-
"type_": TYPE_ENUM["object"],
|
|
93
|
-
},
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def _convert_pydantic_to_genai_function(
|
|
98
|
-
pydantic_model: Type[BaseModel],
|
|
99
|
-
) -> glm.FunctionDeclaration:
|
|
100
|
-
schema = dereference_refs(pydantic_model.schema())
|
|
101
|
-
schema.pop("definitions", None)
|
|
102
|
-
return glm.FunctionDeclaration(
|
|
103
|
-
name=schema["title"],
|
|
104
|
-
description=schema.get("description", ""),
|
|
105
|
-
parameters={
|
|
106
|
-
"properties": {
|
|
107
|
-
k: {
|
|
108
|
-
"type_": TYPE_ENUM[v["type"]],
|
|
109
|
-
"description": v.get("description"),
|
|
110
|
-
}
|
|
111
|
-
for k, v in schema["properties"].items()
|
|
112
|
-
},
|
|
113
|
-
"required": schema["required"],
|
|
114
|
-
"type_": TYPE_ENUM[schema["type"]],
|
|
115
|
-
},
|
|
116
|
-
)
|
|
File without changes
|
|
File without changes
|
{langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/__init__.py
RENAMED
|
File without changes
|
{langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/_common.py
RENAMED
|
File without changes
|
{langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/_enums.py
RENAMED
|
File without changes
|
|
File without changes
|
{langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/genai_aqa.py
RENAMED
|
File without changes
|
|
File without changes
|
{langchain_google_genai-1.0.2 → langchain_google_genai-1.0.3}/langchain_google_genai/py.typed
RENAMED
|
File without changes
|