Skip to content

Comments

feat: improve API reliability with retries and timeouts#82

Merged
shahrear33 merged 1 commit intovlm-run:mainfrom
whereisfarukk:fallback
Dec 23, 2025
Merged

feat: improve API reliability with retries and timeouts#82
shahrear33 merged 1 commit intovlm-run:mainfrom
whereisfarukk:fallback

Conversation

@whereisfarukk
Copy link
Collaborator

  • Add retry mechanism (3 attempts) with exponential backoff
  • Configure timeouts: 60s default, 4min for chat completions
  • Centralize error handling and use NodeOperationError

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @whereisfarukk, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the reliability of API interactions by introducing mechanisms for automatic retries and configurable request timeouts. It also streamlines error reporting by centralizing the logic for processing and re-throwing API-related exceptions, ensuring a more consistent and robust client-side experience.

Highlights

  • API Reliability: Implemented a robust retry mechanism for API requests, allowing up to 3 attempts with exponential backoff for transient network and timeout errors.
  • Configurable Timeouts: Introduced configurable timeouts for API calls, setting a default of 60 seconds and a longer 4-minute timeout specifically for chat completion requests.
  • Centralized Error Handling: Refactored error handling into a dedicated 'handleRequestError' method to consistently parse and report API errors, improving maintainability and clarity.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the VlmRunClient to introduce centralized error handling and a retry mechanism for API requests. A new handleRequestError method was added to parse and throw detailed errors from HTTP responses, replacing previous inline error handling. The makeRequest method now includes retry logic with exponential backoff for transient network errors (e.g., ETIMEDOUT, ECONNRESET), and dynamically sets request timeouts, with a longer duration for chat completion endpoints. New constants for MAX_RETRIES, DEFAULT_TIMEOUT, and CHAT_COMPLETION_TIMEOUT were added to config.ts. Review comments suggest improving the exponential backoff calculation, utilizing NodeOperationError for better n8n integration in the error handler, and extracting the /openai/chat/completions endpoint string into a constant for maintainability.

Comment on lines +222 to +231
const isRetryable =
errorCode === 'ETIMEDOUT' ||
errorCode === 'ECONNRESET' ||
errorCode === 'EPIPE' ||
(!error.response && attempt < MAX_RETRIES); // Network errors without response

if (isRetryable && attempt < MAX_RETRIES) {
const delay = RETRY_DELAY * attempt; // Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay));
continue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There are a couple of areas for improvement in the retry logic:

  1. The PR description mentions "exponential backoff", but the implementation RETRY_DELAY * attempt is a linear backoff. For exponential backoff, you should use something like RETRY_DELAY * Math.pow(2, attempt - 1).
  2. The isRetryable condition (!error.response && attempt < MAX_RETRIES) mixes the concern of what is a retryable error with when to retry. This makes the code harder to read. It's clearer to define isRetryable based only on the error type.
Suggested change
const isRetryable =
errorCode === 'ETIMEDOUT' ||
errorCode === 'ECONNRESET' ||
errorCode === 'EPIPE' ||
(!error.response && attempt < MAX_RETRIES); // Network errors without response
if (isRetryable && attempt < MAX_RETRIES) {
const delay = RETRY_DELAY * attempt; // Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay));
continue;
const isRetryable =
errorCode === 'ETIMEDOUT' ||
errorCode === 'ECONNRESET' ||
errorCode === 'EPIPE' ||
!error.response; // Network errors without response
if (isRetryable && attempt < MAX_RETRIES) {
const delay = RETRY_DELAY * Math.pow(2, attempt - 1); // Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}

Comment on lines +103 to +123
private handleRequestError(error: any): never {
// Extract error details from HTTP response
let errorDetail = error.message || 'Unknown error';
if (error.response?.body) {
try {
const errorBody =
typeof error.response.body === 'string'
? JSON.parse(error.response.body)
: error.response.body;
errorDetail = errorBody.detail || errorBody.message || errorDetail;
} catch {
errorDetail = error.response.body || errorDetail;
}
}

// Throw error with status code if available, otherwise use generic error
if (error.response?.status) {
throw new Error(`HTTP ${error.response.status}: ${errorDetail}`);
}
throw new Error(errorDetail);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The PR description mentions using NodeOperationError, but the implementation throws a generic Error. Using NodeOperationError provides better integration with the n8n ecosystem for error handling. You can get the node instance via this.ef.getNode(). You will also need to import NodeOperationError from n8n-workflow.

private handleRequestError(error: any): never {
		// Extract error details from HTTP response
		let errorDetail = error.message || 'Unknown error';
		if (error.response?.body) {
			try {
				const errorBody =
					typeof error.response.body === 'string'
						? JSON.parse(error.response.body)
						: error.response.body;
				errorDetail = errorBody.detail || errorBody.message || errorDetail;
			} catch {
				errorDetail = error.response.body || errorDetail;
			}
		}

		// Throw error with status code if available, otherwise use generic error
		if (error.response?.status) {
			throw new NodeOperationError(this.ef.getNode(), `HTTP ${error.response.status}: ${errorDetail}`);
		}
		throw new NodeOperationError(this.ef.getNode(), errorDetail);
	}


// Determine timeout based on endpoint
let timeout = DEFAULT_TIMEOUT;
if (endpoint === '/openai/chat/completions') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The endpoint path '/openai/chat/completions' is used as a magic string. To improve maintainability and avoid potential typos, consider defining it as a constant in config.ts and reusing it here and in the chat.completions method.

@shahrear33 shahrear33 merged commit d95c1d8 into vlm-run:main Dec 23, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants