langchain-b12 0.1.8__py3-none-any.whl → 0.1.10__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.
langchain_b12/genai/genai.py
CHANGED
|
@@ -7,10 +7,6 @@ from typing import Any, Literal, cast
|
|
|
7
7
|
from google import genai
|
|
8
8
|
from google.genai import types
|
|
9
9
|
from google.oauth2 import service_account
|
|
10
|
-
from langchain_b12.genai.genai_utils import (
|
|
11
|
-
convert_messages_to_contents,
|
|
12
|
-
parse_response_candidate,
|
|
13
|
-
)
|
|
14
10
|
from langchain_core.callbacks import (
|
|
15
11
|
AsyncCallbackManagerForLLMRun,
|
|
16
12
|
CallbackManagerForLLMRun,
|
|
@@ -40,6 +36,18 @@ from langchain_core.utils.function_calling import (
|
|
|
40
36
|
convert_to_openai_tool,
|
|
41
37
|
)
|
|
42
38
|
from pydantic import BaseModel, ConfigDict, Field
|
|
39
|
+
from tenacity import (
|
|
40
|
+
retry,
|
|
41
|
+
retry_if_exception_type,
|
|
42
|
+
stop_after_attempt,
|
|
43
|
+
stop_never,
|
|
44
|
+
wait_exponential_jitter,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
from langchain_b12.genai.genai_utils import (
|
|
48
|
+
convert_messages_to_contents,
|
|
49
|
+
parse_response_candidate,
|
|
50
|
+
)
|
|
43
51
|
|
|
44
52
|
logger = logging.getLogger(__name__)
|
|
45
53
|
|
|
@@ -76,7 +84,7 @@ class ChatGenAI(BaseChatModel):
|
|
|
76
84
|
seed: int | None = None
|
|
77
85
|
"""Random seed for the generation."""
|
|
78
86
|
max_retries: int | None = Field(default=3)
|
|
79
|
-
"""Maximum number of retries when generation fails. None
|
|
87
|
+
"""Maximum number of retries when generation fails. None retries indefinitely."""
|
|
80
88
|
safety_settings: list[types.SafetySetting] | None = None
|
|
81
89
|
"""The default safety settings to use for all generations.
|
|
82
90
|
|
|
@@ -175,24 +183,10 @@ class ChatGenAI(BaseChatModel):
|
|
|
175
183
|
run_manager: CallbackManagerForLLMRun | None = None,
|
|
176
184
|
**kwargs: Any,
|
|
177
185
|
) -> ChatResult:
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
messages, stop=stop, run_manager=run_manager, **kwargs
|
|
183
|
-
)
|
|
184
|
-
return generate_from_stream(stream_iter)
|
|
185
|
-
except Exception as e: # noqa: BLE001
|
|
186
|
-
if self.max_retries is None or attempts >= self.max_retries:
|
|
187
|
-
raise
|
|
188
|
-
attempts += 1
|
|
189
|
-
logger.warning(
|
|
190
|
-
"ChatGenAI._generate failed (attempt %d/%d). "
|
|
191
|
-
"Retrying... Error: %s",
|
|
192
|
-
attempts,
|
|
193
|
-
self.max_retries,
|
|
194
|
-
e,
|
|
195
|
-
)
|
|
186
|
+
stream_iter = self._stream(
|
|
187
|
+
messages, stop=stop, run_manager=run_manager, **kwargs
|
|
188
|
+
)
|
|
189
|
+
return generate_from_stream(stream_iter)
|
|
196
190
|
|
|
197
191
|
async def _agenerate(
|
|
198
192
|
self,
|
|
@@ -201,24 +195,10 @@ class ChatGenAI(BaseChatModel):
|
|
|
201
195
|
run_manager: AsyncCallbackManagerForLLMRun | None = None,
|
|
202
196
|
**kwargs: Any,
|
|
203
197
|
) -> ChatResult:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
messages, stop=stop, run_manager=run_manager, **kwargs
|
|
209
|
-
)
|
|
210
|
-
return await agenerate_from_stream(stream_iter)
|
|
211
|
-
except Exception as e: # noqa: BLE001
|
|
212
|
-
if self.max_retries is None or attempts >= self.max_retries:
|
|
213
|
-
raise
|
|
214
|
-
attempts += 1
|
|
215
|
-
logger.warning(
|
|
216
|
-
"ChatGenAI._agenerate failed (attempt %d/%d). "
|
|
217
|
-
"Retrying... Error: %s",
|
|
218
|
-
attempts,
|
|
219
|
-
self.max_retries,
|
|
220
|
-
e,
|
|
221
|
-
)
|
|
198
|
+
stream_iter = self._astream(
|
|
199
|
+
messages, stop=stop, run_manager=run_manager, **kwargs
|
|
200
|
+
)
|
|
201
|
+
return await agenerate_from_stream(stream_iter)
|
|
222
202
|
|
|
223
203
|
def _stream(
|
|
224
204
|
self,
|
|
@@ -228,26 +208,64 @@ class ChatGenAI(BaseChatModel):
|
|
|
228
208
|
**kwargs: Any,
|
|
229
209
|
) -> Iterator[ChatGenerationChunk]:
|
|
230
210
|
system_message, contents = self._prepare_request(messages=messages)
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
),
|
|
247
|
-
**kwargs,
|
|
211
|
+
|
|
212
|
+
@retry(
|
|
213
|
+
reraise=True,
|
|
214
|
+
stop=stop_after_attempt(self.max_retries + 1)
|
|
215
|
+
if self.max_retries is not None
|
|
216
|
+
else stop_never,
|
|
217
|
+
wait=wait_exponential_jitter(initial=1, max=60),
|
|
218
|
+
retry=retry_if_exception_type(Exception),
|
|
219
|
+
before_sleep=lambda retry_state: logger.warning(
|
|
220
|
+
"ChatGenAI._stream failed to start (attempt %d/%s). "
|
|
221
|
+
"Retrying in %.2fs... Error: %s",
|
|
222
|
+
retry_state.attempt_number,
|
|
223
|
+
self.max_retries + 1 if self.max_retries is not None else "∞",
|
|
224
|
+
retry_state.next_action.sleep,
|
|
225
|
+
retry_state.outcome.exception(),
|
|
248
226
|
),
|
|
249
227
|
)
|
|
250
|
-
|
|
228
|
+
def _initiate_stream() -> tuple[
|
|
229
|
+
ChatGenerationChunk,
|
|
230
|
+
Iterator[types.GenerateContentResponse],
|
|
231
|
+
UsageMetadata | None,
|
|
232
|
+
]:
|
|
233
|
+
"""Initialize stream and fetch first chunk. Retries only apply here."""
|
|
234
|
+
response_iter = self.client.models.generate_content_stream(
|
|
235
|
+
model=self.model_name,
|
|
236
|
+
contents=contents,
|
|
237
|
+
config=types.GenerateContentConfig(
|
|
238
|
+
system_instruction=system_message,
|
|
239
|
+
temperature=self.temperature,
|
|
240
|
+
top_k=self.top_k,
|
|
241
|
+
top_p=self.top_p,
|
|
242
|
+
max_output_tokens=self.max_output_tokens,
|
|
243
|
+
candidate_count=self.n,
|
|
244
|
+
stop_sequences=stop or self.stop,
|
|
245
|
+
safety_settings=self.safety_settings,
|
|
246
|
+
thinking_config=self.thinking_config,
|
|
247
|
+
automatic_function_calling=types.AutomaticFunctionCallingConfig(
|
|
248
|
+
disable=True,
|
|
249
|
+
),
|
|
250
|
+
**kwargs,
|
|
251
|
+
),
|
|
252
|
+
)
|
|
253
|
+
# Fetch first chunk to ensure connection is established
|
|
254
|
+
first_response = next(iter(response_iter))
|
|
255
|
+
first_chunk, total_usage = self._gemini_chunk_to_generation_chunk(
|
|
256
|
+
first_response, prev_total_usage=None
|
|
257
|
+
)
|
|
258
|
+
return first_chunk, response_iter, total_usage
|
|
259
|
+
|
|
260
|
+
# Retry only covers stream initialization and first chunk
|
|
261
|
+
first_chunk, response_iter, total_lc_usage = _initiate_stream()
|
|
262
|
+
|
|
263
|
+
# Yield first chunk
|
|
264
|
+
if run_manager and isinstance(first_chunk.message.content, str):
|
|
265
|
+
run_manager.on_llm_new_token(first_chunk.message.content)
|
|
266
|
+
yield first_chunk
|
|
267
|
+
|
|
268
|
+
# Continue streaming without retry (retries during streaming are not well defined)
|
|
251
269
|
for response_chunk in response_iter:
|
|
252
270
|
chunk, total_lc_usage = self._gemini_chunk_to_generation_chunk(
|
|
253
271
|
response_chunk, prev_total_usage=total_lc_usage
|
|
@@ -264,27 +282,65 @@ class ChatGenAI(BaseChatModel):
|
|
|
264
282
|
**kwargs: Any,
|
|
265
283
|
) -> AsyncIterator[ChatGenerationChunk]:
|
|
266
284
|
system_message, contents = self._prepare_request(messages=messages)
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
),
|
|
283
|
-
**kwargs,
|
|
285
|
+
|
|
286
|
+
@retry(
|
|
287
|
+
reraise=True,
|
|
288
|
+
stop=stop_after_attempt(self.max_retries + 1)
|
|
289
|
+
if self.max_retries is not None
|
|
290
|
+
else stop_never,
|
|
291
|
+
wait=wait_exponential_jitter(initial=1, max=60),
|
|
292
|
+
retry=retry_if_exception_type(Exception),
|
|
293
|
+
before_sleep=lambda retry_state: logger.warning(
|
|
294
|
+
"ChatGenAI._astream failed to start (attempt %d/%s). "
|
|
295
|
+
"Retrying in %.2fs... Error: %s",
|
|
296
|
+
retry_state.attempt_number,
|
|
297
|
+
self.max_retries + 1 if self.max_retries is not None else "∞",
|
|
298
|
+
retry_state.next_action.sleep,
|
|
299
|
+
retry_state.outcome.exception(),
|
|
284
300
|
),
|
|
285
301
|
)
|
|
286
|
-
|
|
287
|
-
|
|
302
|
+
async def _initiate_stream() -> tuple[
|
|
303
|
+
ChatGenerationChunk,
|
|
304
|
+
AsyncIterator[types.GenerateContentResponse],
|
|
305
|
+
UsageMetadata | None,
|
|
306
|
+
]:
|
|
307
|
+
"""Initialize stream and fetch first chunk. Retries only apply here."""
|
|
308
|
+
response_iter = await self.client.aio.models.generate_content_stream(
|
|
309
|
+
model=self.model_name,
|
|
310
|
+
contents=contents,
|
|
311
|
+
config=types.GenerateContentConfig(
|
|
312
|
+
system_instruction=system_message,
|
|
313
|
+
temperature=self.temperature,
|
|
314
|
+
top_k=self.top_k,
|
|
315
|
+
top_p=self.top_p,
|
|
316
|
+
max_output_tokens=self.max_output_tokens,
|
|
317
|
+
candidate_count=self.n,
|
|
318
|
+
stop_sequences=stop or self.stop,
|
|
319
|
+
safety_settings=self.safety_settings,
|
|
320
|
+
thinking_config=self.thinking_config,
|
|
321
|
+
automatic_function_calling=types.AutomaticFunctionCallingConfig(
|
|
322
|
+
disable=True,
|
|
323
|
+
),
|
|
324
|
+
**kwargs,
|
|
325
|
+
),
|
|
326
|
+
)
|
|
327
|
+
# Fetch first chunk to ensure connection is established
|
|
328
|
+
first_response = await response_iter.__anext__()
|
|
329
|
+
first_chunk, total_usage = self._gemini_chunk_to_generation_chunk(
|
|
330
|
+
first_response, prev_total_usage=None
|
|
331
|
+
)
|
|
332
|
+
return first_chunk, response_iter, total_usage
|
|
333
|
+
|
|
334
|
+
# Retry only covers stream initialization and first chunk
|
|
335
|
+
first_chunk, response_iter, total_lc_usage = await _initiate_stream()
|
|
336
|
+
|
|
337
|
+
# Yield first chunk
|
|
338
|
+
if run_manager and isinstance(first_chunk.message.content, str):
|
|
339
|
+
await run_manager.on_llm_new_token(first_chunk.message.content)
|
|
340
|
+
yield first_chunk
|
|
341
|
+
|
|
342
|
+
# Continue streaming without retry (retries during streaming are not well defined)
|
|
343
|
+
async for response_chunk in response_iter:
|
|
288
344
|
chunk, total_lc_usage = self._gemini_chunk_to_generation_chunk(
|
|
289
345
|
response_chunk, prev_total_usage=total_lc_usage
|
|
290
346
|
)
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langchain-b12
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.10
|
|
4
4
|
Summary: A reusable collection of tools and implementations for Langchain
|
|
5
5
|
Author-email: Vincent Min <vincent.min@b12-consulting.com>
|
|
6
6
|
Requires-Python: >=3.11
|
|
7
7
|
Requires-Dist: langchain-core>=0.3.60
|
|
8
|
+
Requires-Dist: pytest-anyio>=0.0.0
|
|
9
|
+
Requires-Dist: tenacity>=9.1.2
|
|
8
10
|
Description-Content-Type: text/markdown
|
|
9
11
|
|
|
10
12
|
# Langchain B12
|
|
@@ -2,8 +2,8 @@ langchain_b12/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
2
2
|
langchain_b12/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
langchain_b12/citations/citations.py,sha256=ZQvYayjQXIUaRosJ0qwL3Nc7kC8sBzmaIkE-BOslaVI,12261
|
|
4
4
|
langchain_b12/genai/embeddings.py,sha256=h0Z-5PltDW9q79AjSrLemsz-_QKMB-043XXDvYSRQds,3483
|
|
5
|
-
langchain_b12/genai/genai.py,sha256=
|
|
5
|
+
langchain_b12/genai/genai.py,sha256=JoivVvUBl-mvRpl9UTC_Q6-8N4DkEHK1f7-bqI_V6Y4,20786
|
|
6
6
|
langchain_b12/genai/genai_utils.py,sha256=tA6UiJURK25-11vtaX4768UV47jDCYwVKIIWydD4Egw,10736
|
|
7
|
-
langchain_b12-0.1.
|
|
8
|
-
langchain_b12-0.1.
|
|
9
|
-
langchain_b12-0.1.
|
|
7
|
+
langchain_b12-0.1.10.dist-info/METADATA,sha256=LcMlsuxt4CO9Q-FeGqR3tx2mhmEhEMAagkWvBTmUtbo,1271
|
|
8
|
+
langchain_b12-0.1.10.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
9
|
+
langchain_b12-0.1.10.dist-info/RECORD,,
|