Skip to content

fix: add batch item dispatching to specific handlers based on URL - #1495

Merged
diegosouzapw merged 1 commit into
cheaper-inference:release/v3.7.0from
hartmark:feature/openai-batching-embedding-fix
Apr 22, 2026
Merged

diegosouzapw merged 1 commit into
cheaper-inference:release/v3.7.0from
hartmark:feature/openai-batching-embedding-fix

Conversation

@hartmark

Copy link
Copy Markdown
Contributor

Summary

My previous PR #1479 had one issue, it always routed to the chat endpoint, so for example bulking embeddings failed.
This PR fixes that issue.

Validation

  • npm run lint
  • npm run test:unit
  • npm run test:coverage
  • Coverage is still >= 60% for statements, lines, functions, and branches
  • SonarQube PR analysis is green or any remaining issues are explicitly documented below

Tests Added Or Updated

Added tests for affected code

@hartmark
hartmark requested a review from diegosouzapw as a code owner April 21, 2026 23:19

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

Copy link
Copy Markdown
Contributor

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 introduces a dispatchBatchItem function to the batch processor, enabling support for multiple API endpoints including embeddings, moderations, image generation, and video generation. Previously, the batch processor was limited to chat completions. The changes also include manual HTTP tests and a unit test to verify correct routing. However, the dispatchBatchItem function contains a bug where it fails to strictly force stream: false for chat requests, which will cause parsing errors during batch execution. Additionally, there are concerns regarding the routing of image edits and video endpoints to generation-specific handlers, as these typically require different request formats.

Comment on lines +186 to +223
async function dispatchBatchItem(
url: string,
headers: Headers,
body: Record<string, unknown>
): Promise<Response> {
const request = new Request(`http://localhost${url}`, {
method: "POST",
headers,
body: JSON.stringify(body),
});

switch (url) {
case "/v1/embeddings":
return handleEmbeddingsRoute(request);
case "/v1/moderations":
return handleModerationsRoute(request);
case "/v1/images/generations":
case "/v1/images/edits":
return handleImagesGenerationsRoute(request);
case "/v1/videos":
case "/v1/videos/generations":
return handleVideosGenerationsRoute(request);
// /v1/chat/completions, /v1/completions, /v1/responses
default: {
return handleChat(
new Request(`http://localhost${url}`, {
method: "POST",
headers,
body: JSON.stringify({
...body,
// BATCH-SPECIFIC: Force stream: false — batches don't support SSE responses
stream: body.stream ?? false,
}),
})
);
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The dispatchBatchItem function contains a bug where stream: body.stream ?? false preserves stream: true if present in the input body. Since batch processing does not support SSE responses and expects a standard JSON response, this will cause failures when the processor attempts to parse the stream as JSON. Additionally, the Request object is redundantly created at the start and then discarded in the default case.

I suggest refactoring the function to determine the final body first and then create the request once, while ensuring stream is strictly forced to false for chat endpoints.

async function dispatchBatchItem(
  url: string,
  headers: Headers,
  body: Record<string, unknown>
): Promise<Response> {
  // BATCH-SPECIFIC: Force stream: false — batches don't support SSE responses.
  // This is primarily relevant for chat/completion endpoints.
  const isChatEndpoint = !["/v1/embeddings", "/v1/moderations", "/v1/images/generations", "/v1/images/edits", "/v1/videos", "/v1/videos/generations"].includes(url);
  
  const request = new Request(`http://localhost${url}`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      ...body,
      ...(isChatEndpoint ? { stream: false } : {}),
    }),
  });

  switch (url) {
    case "/v1/embeddings":
      return handleEmbeddingsRoute(request);
    case "/v1/moderations":
      return handleModerationsRoute(request);
    case "/v1/images/generations":
    case "/v1/images/edits":
      return handleImagesGenerationsRoute(request);
    case "/v1/videos":
    case "/v1/videos/generations":
      return handleVideosGenerationsRoute(request);
    default:
      return handleChat(request);
  }
}

Comment on lines +203 to +207
case "/v1/images/edits":
return handleImagesGenerationsRoute(request);
case "/v1/videos":
case "/v1/videos/generations":
return handleVideosGenerationsRoute(request);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Routing /v1/images/edits and /v1/videos to handlers imported from generations/route is likely to cause issues. These endpoints typically expect different request payloads (e.g., images/edits usually requires multipart data for image files) and follow different logic than standard generation endpoints. If these handlers are strictly for generations, they will likely reject requests to these other paths.

@diegosouzapw
diegosouzapw merged commit 098639e into cheaper-inference:release/v3.7.0 Apr 22, 2026
2 checks passed
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Thanks @hartmark for this great contribution! 🎉 It is now officially merged into the release/v3.7.0 branch and will be part of the next release. We appreciate your effort!

@diegosouzapw diegosouzapw mentioned this pull request Apr 22, 2026
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
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