- 
                Notifications
    You must be signed in to change notification settings 
- Fork 14
Improve timeout and retry handling #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds timeout and retry handling capabilities to the langchain-gradient library. The changes implement configurable timeout and retry behavior for API requests, along with proper error handling and documentation.
- Adds custom exception classes (APITimeoutError,APIRetryError) for better error handling
- Implements a RetryConfigclass with exponential backoff support
- Updates ChatGradientto support timeout and retry configuration through the Gradient SDK
- Adds comprehensive test coverage for timeout and retry scenarios
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description | 
|---|---|
| tests/unit_tests/test_timeout_retry.py | New test file covering timeout configuration, retry logic, and error handling scenarios | 
| langchain_gradient/retry_config.py | New module implementing retry configuration with exponential backoff | 
| langchain_gradient/exceptions.py | New module defining custom exception classes for timeout and retry errors | 
| langchain_gradient/chat_models.py | Updated to support timeout/retry configuration and enhanced error handling with gradient SDK exceptions | 
| docs/timeout_retry-examples.md | New documentation file with usage examples for timeout and retry configuration | 
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| raise APIRetryError( | ||
| f"Failed after {retry_config.max_retries + 1} attempts", | ||
| attempts=retry_config.max_retries + 1 | ||
| ) from last_exception | 
    
      
    
      Copilot
AI
    
    
    
      Oct 28, 2025 
    
  
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The APIRetryError exception is used but not imported. This will cause a NameError at runtime when the max retries are exceeded.
| max_retries: int = 2 | ||
| """Max number of retries.""" | ||
| timeout: Optional[float] = Field(default=60.0) | ||
| """Timeout for requests in seconds. Defaults to 60.0 seconds.""" | 
    
      
    
      Copilot
AI
    
    
    
      Oct 28, 2025 
    
  
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring indicates timeout defaults to 60.0 seconds, but the class-level docstring at line 73 states it 'Defaults to 60.0 seconds (Gradient SDK default)'. The actual Gradient SDK default should be verified, as the code now explicitly sets 60.0 instead of allowing the SDK to use its own default. Consider using None if you want to rely on the SDK's default, or clarify that this is now a library-specific default.
| """Timeout for requests in seconds. Defaults to 60.0 seconds.""" | |
| """Timeout for requests in seconds. Defaults to 60.0 seconds (library default).""" | 
| from typing import Any, Dict, Iterator, List, Optional, Union | ||
|  | ||
| from gradient import Gradient | ||
| import gradient | 
    
      
    
      Copilot
AI
    
    
    
      Oct 28, 2025 
    
  
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The gradient module is imported twice: once at line 6 as from gradient import Gradient and again at line 7 as import gradient. The second import is only used for exception handling. Consider organizing imports more clearly or adding a comment explaining why both import styles are needed.
| from langchain_gradient import ChatGradient | ||
| from langchain_gradient.exceptions import APITimeoutError, APIRetryError | ||
|  | ||
| Initialize with timeout and retry configuration | ||
| llm = ChatGradient( | ||
| model="llama3.3-70b-instruct", | ||
| timeout=30.0, | ||
| max_retries=3 | ||
| ) | ||
|  | ||
| try: | ||
| response = llm.invoke("Hello world!") | ||
| print(response) | ||
| except APITimeoutError as e: | ||
| print(f"Request timed out: {e}") | ||
| except APIRetryError as e: | ||
| print(f"Max retries exceeded: {e}") | ||
|  | ||
|  | ||
| ## Advanced Configuration | ||
|  | ||
| from langchain_gradient import ChatGradient | ||
| from langchain_gradient.retry_config import RetryConfig | ||
|  | ||
| Custom retry configuration with exponential backoff | ||
| custom_retry = RetryConfig( | ||
| max_retries=5, | ||
| initial_delay=1.0, | ||
| max_delay=60.0, | ||
| exponential_base=2.0, | ||
| jitter=True | ||
| ) | ||
|  | ||
| llm = ChatGradient( | ||
| model="llama3.3-70b-instruct", | ||
| timeout=60.0, | ||
| retry_config=custom_retry | ||
| ) | ||
|  | ||
|  | 
    
      
    
      Copilot
AI
    
    
    
      Oct 28, 2025 
    
  
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing markdown code fence markers around the Python code. The code examples should be wrapped in triple backticks with the python language identifier for proper rendering.
| from langchain_gradient import ChatGradient | |
| from langchain_gradient.exceptions import APITimeoutError, APIRetryError | |
| Initialize with timeout and retry configuration | |
| llm = ChatGradient( | |
| model="llama3.3-70b-instruct", | |
| timeout=30.0, | |
| max_retries=3 | |
| ) | |
| try: | |
| response = llm.invoke("Hello world!") | |
| print(response) | |
| except APITimeoutError as e: | |
| print(f"Request timed out: {e}") | |
| except APIRetryError as e: | |
| print(f"Max retries exceeded: {e}") | |
| ## Advanced Configuration | |
| from langchain_gradient import ChatGradient | |
| from langchain_gradient.retry_config import RetryConfig | |
| Custom retry configuration with exponential backoff | |
| custom_retry = RetryConfig( | |
| max_retries=5, | |
| initial_delay=1.0, | |
| max_delay=60.0, | |
| exponential_base=2.0, | |
| jitter=True | |
| ) | |
| llm = ChatGradient( | |
| model="llama3.3-70b-instruct", | |
| timeout=60.0, | |
| retry_config=custom_retry | |
| ) | |
| ```python | |
| from langchain_gradient import ChatGradient | |
| from langchain_gradient.exceptions import APITimeoutError, APIRetryError | |
| # Initialize with timeout and retry configuration | |
| llm = ChatGradient( | |
| model="llama3.3-70b-instruct", | |
| timeout=30.0, | |
| max_retries=3 | |
| ) | |
| try: | |
| response = llm.invoke("Hello world!") | |
| print(response) | |
| except APITimeoutError as e: | |
| print(f"Request timed out: {e}") | |
| except APIRetryError as e: | |
| print(f"Max retries exceeded: {e}") | 
Advanced Configuration
from langchain_gradient import ChatGradient
from langchain_gradient.retry_config import RetryConfig
# Custom retry configuration with exponential backoff
custom_retry = RetryConfig(
    max_retries=5,
    initial_delay=1.0,
    max_delay=60.0,
    exponential_base=2.0,
    jitter=True
)
llm = ChatGradient(
    model="llama3.3-70b-instruct",
    timeout=60.0,
    retry_config=custom_retry
)| attempts=retry_config.max_retries + 1 | ||
| ) from last_exception | ||
|  | ||
| return None | 
    
      
    
      Copilot
AI
    
    
    
      Oct 28, 2025 
    
  
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This return statement is unreachable because the for loop will either return successfully or raise an exception. Remove this line to improve code clarity.
| return None | 
solved #7