langchain-b12 0.1.9__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.
@@ -84,7 +84,7 @@ class ChatGenAI(BaseChatModel):
84
84
  seed: int | None = None
85
85
  """Random seed for the generation."""
86
86
  max_retries: int | None = Field(default=3)
87
- """Maximum number of retries when generation fails. None disables retries."""
87
+ """Maximum number of retries when generation fails. None retries indefinitely."""
88
88
  safety_settings: list[types.SafetySetting] | None = None
89
89
  """The default safety settings to use for all generations.
90
90
 
@@ -183,29 +183,10 @@ class ChatGenAI(BaseChatModel):
183
183
  run_manager: CallbackManagerForLLMRun | None = None,
184
184
  **kwargs: Any,
185
185
  ) -> ChatResult:
186
- @retry(
187
- reraise=True,
188
- stop=stop_after_attempt(self.max_retries + 1)
189
- if self.max_retries is not None
190
- else stop_never,
191
- wait=wait_exponential_jitter(initial=1, max=60),
192
- retry=retry_if_exception_type(Exception),
193
- before_sleep=lambda retry_state: logger.warning(
194
- "ChatGenAI._generate failed (attempt %d/%s). "
195
- "Retrying in %.2fs... Error: %s",
196
- retry_state.attempt_number,
197
- self.max_retries + 1 if self.max_retries is not None else "∞",
198
- retry_state.next_action.sleep,
199
- retry_state.outcome.exception(),
200
- ),
186
+ stream_iter = self._stream(
187
+ messages, stop=stop, run_manager=run_manager, **kwargs
201
188
  )
202
- def _generate_with_retry() -> ChatResult:
203
- stream_iter = self._stream(
204
- messages, stop=stop, run_manager=run_manager, **kwargs
205
- )
206
- return generate_from_stream(stream_iter)
207
-
208
- return _generate_with_retry()
189
+ return generate_from_stream(stream_iter)
209
190
 
210
191
  async def _agenerate(
211
192
  self,
@@ -214,6 +195,20 @@ class ChatGenAI(BaseChatModel):
214
195
  run_manager: AsyncCallbackManagerForLLMRun | None = None,
215
196
  **kwargs: Any,
216
197
  ) -> ChatResult:
198
+ stream_iter = self._astream(
199
+ messages, stop=stop, run_manager=run_manager, **kwargs
200
+ )
201
+ return await agenerate_from_stream(stream_iter)
202
+
203
+ def _stream(
204
+ self,
205
+ messages: list[BaseMessage],
206
+ stop: list[str] | None = None,
207
+ run_manager: CallbackManagerForLLMRun | None = None,
208
+ **kwargs: Any,
209
+ ) -> Iterator[ChatGenerationChunk]:
210
+ system_message, contents = self._prepare_request(messages=messages)
211
+
217
212
  @retry(
218
213
  reraise=True,
219
214
  stop=stop_after_attempt(self.max_retries + 1)
@@ -222,7 +217,7 @@ class ChatGenAI(BaseChatModel):
222
217
  wait=wait_exponential_jitter(initial=1, max=60),
223
218
  retry=retry_if_exception_type(Exception),
224
219
  before_sleep=lambda retry_state: logger.warning(
225
- "ChatGenAI._agenerate failed (attempt %d/%s). "
220
+ "ChatGenAI._stream failed to start (attempt %d/%s). "
226
221
  "Retrying in %.2fs... Error: %s",
227
222
  retry_state.attempt_number,
228
223
  self.max_retries + 1 if self.max_retries is not None else "∞",
@@ -230,42 +225,47 @@ class ChatGenAI(BaseChatModel):
230
225
  retry_state.outcome.exception(),
231
226
  ),
232
227
  )
233
- async def _agenerate_with_retry() -> ChatResult:
234
- stream_iter = self._astream(
235
- messages, stop=stop, run_manager=run_manager, **kwargs
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
+ ),
236
252
  )
237
- return await agenerate_from_stream(stream_iter)
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
238
259
 
239
- return await _agenerate_with_retry()
260
+ # Retry only covers stream initialization and first chunk
261
+ first_chunk, response_iter, total_lc_usage = _initiate_stream()
240
262
 
241
- def _stream(
242
- self,
243
- messages: list[BaseMessage],
244
- stop: list[str] | None = None,
245
- run_manager: CallbackManagerForLLMRun | None = None,
246
- **kwargs: Any,
247
- ) -> Iterator[ChatGenerationChunk]:
248
- system_message, contents = self._prepare_request(messages=messages)
249
- response_iter = self.client.models.generate_content_stream(
250
- model=self.model_name,
251
- contents=contents,
252
- config=types.GenerateContentConfig(
253
- system_instruction=system_message,
254
- temperature=self.temperature,
255
- top_k=self.top_k,
256
- top_p=self.top_p,
257
- max_output_tokens=self.max_output_tokens,
258
- candidate_count=self.n,
259
- stop_sequences=stop or self.stop,
260
- safety_settings=self.safety_settings,
261
- thinking_config=self.thinking_config,
262
- automatic_function_calling=types.AutomaticFunctionCallingConfig(
263
- disable=True,
264
- ),
265
- **kwargs,
266
- ),
267
- )
268
- total_lc_usage = None
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)
269
269
  for response_chunk in response_iter:
270
270
  chunk, total_lc_usage = self._gemini_chunk_to_generation_chunk(
271
271
  response_chunk, prev_total_usage=total_lc_usage
@@ -282,27 +282,65 @@ class ChatGenAI(BaseChatModel):
282
282
  **kwargs: Any,
283
283
  ) -> AsyncIterator[ChatGenerationChunk]:
284
284
  system_message, contents = self._prepare_request(messages=messages)
285
- response_iter = self.client.aio.models.generate_content_stream(
286
- model=self.model_name,
287
- contents=contents,
288
- config=types.GenerateContentConfig(
289
- system_instruction=system_message,
290
- temperature=self.temperature,
291
- top_k=self.top_k,
292
- top_p=self.top_p,
293
- max_output_tokens=self.max_output_tokens,
294
- candidate_count=self.n,
295
- stop_sequences=stop or self.stop,
296
- safety_settings=self.safety_settings,
297
- thinking_config=self.thinking_config,
298
- automatic_function_calling=types.AutomaticFunctionCallingConfig(
299
- disable=True,
300
- ),
301
- **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(),
302
300
  ),
303
301
  )
304
- total_lc_usage = None
305
- async for response_chunk in await response_iter:
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:
306
344
  chunk, total_lc_usage = self._gemini_chunk_to_generation_chunk(
307
345
  response_chunk, prev_total_usage=total_lc_usage
308
346
  )
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langchain-b12
3
- Version: 0.1.9
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
8
9
  Requires-Dist: tenacity>=9.1.2
9
10
  Description-Content-Type: text/markdown
10
11
 
@@ -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=zZdGwkkaxobnA1jT07MvWHaIHKeBis4X1zJ8o5KrnHk,18841
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.9.dist-info/METADATA,sha256=CpmYdZwkej43d-DvTOR8FU65QSHAZ4iiFu7Ale8Gy0I,1235
8
- langchain_b12-0.1.9.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
9
- langchain_b12-0.1.9.dist-info/RECORD,,
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,,