Skip to content

refactor(chat): modularize chat handler with incremental modules - #1450

Closed
steebchen wants to merge 4 commits into
mainfrom
terragon/refactor-chat-handler-incremental-9nl3n9
Closed

steebchen wants to merge 4 commits into
mainfrom
terragon/refactor-chat-handler-incremental-9nl3n9

Conversation

@steebchen

@steebchen steebchen commented Jan 14, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Incrementally refactor the chat handler by extracting schemas and helper utilities into dedicated modules for better maintainability.

Changes

Core modularization

  • Added new Completions schema module: apps/gateway/src/chat/schemas/completions.ts
    • Exposes completionsRequestSchema and CompletionsRequest type
  • Added new utility modules:
    • apps/gateway/src/chat/tools/estimate-tokens-from-content.ts (token estimation based on content length)
    • apps/gateway/src/chat/tools/convert-images-to-base64.ts (convert external image URLs to data URLs)
    • apps/gateway/src/chat/tools/is-model-truly-free.ts (determine if a model is truly free)
  • apps/gateway/src/chat/chat.ts updated to import and use the new modules:
    • Import completionsRequestSchema from ./schemas/completions.js
    • Import convertImagesToBase64 from ./tools/convert-images-to-base64.js
    • Import estimateTokensFromContent from ./tools/estimate-tokens-from-content.js
    • Import isModelTrulyFree from ./tools/is-model-truly-free.js

Backward compatibility

  • Re-export estimateTokensFromContent from ./tools/estimate-tokens-from-content.js to preserve existing imports

Code cleanup

  • Removed in-file definitions for:
    • completionsRequestSchema (moved to new schemas module)
    • estimateTokensFromContent (moved to new helpers)
    • isModelTrulyFree (moved to new helpers)
    • Large block for image conversion logic (moved to convert-images-to-base64.ts)
  • Updated imports in chat.ts to reflect the new modular structure

Why

  • Improves maintainability by isolating schemas and business logic
  • Enables incremental refactors without large single-shot changes
  • Keeps compatibility via a deliberate re-export for existing imports

Tests / Validation

  • Code builds successfully with new module imports
  • Chat route compiles with updated imports
  • Completions request schema resolves from new module
  • Image URL normalization logic is available via new convert-images-to-base64 utility
  • Token estimation utility is available via new module and backward-compatible re-export
  • Model-free checks can use isModelTrulyFree from the new module when needed

Additional notes

  • This refactor lays groundwork for additional incremental module extractions in the chat handler without changing runtime behavior.

🌿 Generated by Terry


ℹ️ Tag @terragon-labs to ask questions and address PR feedback

📎 Task: https://www.terragonlabs.com/task/d400513a-5888-4144-8eda-2ff9fc79ff08

Summary by CodeRabbit

  • Refactor

    • Chat logic reorganized: validation, image handling, token estimation, and model-free checks moved to shared utilities for easier maintenance.
  • New Features

    • Added a comprehensive OpenAPI-ready request schema for chat completions to improve validation and docs.
    • External image URLs are now converted to embedded data URLs for more reliable image messages.
    • Added token estimation and true-free-model detection to improve request handling and model selection.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings January 14, 2026 22:06
@github-actions github-actions Bot changed the title Refactor chat.ts: modularize schemas and helpers refactor(chat): modularize schemas Jan 14, 2026
@coderabbitai

coderabbitai Bot commented Jan 14, 2026 •

Copy link
Copy Markdown
Contributor

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Replaces in-file chat utilities and the embedded completions schema with imports from new modules, and adds three utility modules (image conversion, token estimation, free-model check) plus a standalone completions schema file. Tests/imports updated accordingly.

Changes

Cohort / File(s) Summary
Main module
apps/gateway/src/chat/chat.ts
Replaced large in-file implementations with imports: completionsRequestSchema from ./schemas/completions, convertImagesToBase64 from ./tools/convert-images-to-base64, estimateTokensFromContent from ./tools/estimate-tokens-from-content, and isModelTrulyFree from ./tools/is-model-truly-free.
Schema
apps/gateway/src/chat/schemas/completions.ts
Added completionsRequestSchema (zod + OpenAPI metadata) and exported CompletionsRequest type validating comple­tions request shape and options.
Tools / Utilities
apps/gateway/src/chat/tools/convert-images-to-base64.ts, apps/gateway/src/chat/tools/estimate-tokens-from-content.ts, apps/gateway/src/chat/tools/is-model-truly-free.ts
New utilities: convertImagesToBase64 (fetch external images → data URLs with error logging), estimateTokensFromContent (approx tokens = max(1, round(len/4))), and isModelTrulyFree (checks model.free and provider per-request prices).
Tests / Imports
apps/gateway/src/lib/prompt-tokens.spec.ts
Updated import to use @/chat/tools/estimate-tokens-from-content.js instead of the previous in-file export.
UI minor
apps/ui/src/components/landing/hero-rsc.tsx
Consolidated duplicate import of allMigrations to top-level; no behavioral change.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

auto-merge

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: refactoring the chat handler by extracting utilities and schemas into dedicated modular files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

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 refactors the chat handler by extracting schemas and utility functions from the main chat.ts file into dedicated modules, improving code organization and maintainability.

Changes:

  • Created a new schemas module (completions.ts) with the completions request schema and type
  • Extracted three utility functions into separate modules: estimateTokensFromContent, convertImagesToBase64, and isModelTrulyFree
  • Updated chat.ts to import these new modules and maintained backward compatibility via re-export

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
apps/gateway/src/chat/schemas/completions.ts New schema module containing completions request schema and type definition
apps/gateway/src/chat/tools/estimate-tokens-from-content.ts Token estimation utility extracted from chat.ts
apps/gateway/src/chat/tools/convert-images-to-base64.ts Image URL to base64 conversion utility extracted from chat.ts
apps/gateway/src/chat/tools/is-model-truly-free.ts Model free-tier check utility extracted from chat.ts
apps/gateway/src/chat/chat.ts Main chat handler updated to import from new modules with backward-compatible re-export

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/gateway/src/chat/tools/convert-images-to-base64.ts
Comment on lines +1 to +6
/**
* Estimates tokens from content length using simple division
*/
export function estimateTokensFromContent(content: string): number {
return Math.max(1, Math.round(content.length / 4));
}

Copilot AI Jan 14, 2026

Copy link

Choose a reason for hiding this comment

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

This extracted utility function lacks dedicated test coverage. While it's tested indirectly through apps/gateway/src/lib/prompt-tokens.spec.ts, consider adding unit tests in the tools folder similar to heal-json-response.spec.ts to ensure the function works correctly in isolation.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +49
import { logger } from "@llmgateway/logger";

import type { ImageObject } from "./types.js";

/**
* Converts external image URLs to base64 data URLs
* Used for providers like Alibaba that return external URLs instead of base64
*/
export async function convertImagesToBase64(
images: ImageObject[],
): Promise<ImageObject[]> {
return await Promise.all(
images.map(async (image): Promise<ImageObject> => {
const url = image.image_url.url;
// Skip if already a data URL
if (url.startsWith("data:")) {
return image;
}

try {
const response = await fetch(url);
if (!response.ok) {
logger.warn("Failed to fetch image for base64 conversion", {
url,
status: response.status,
});
return image;
}

const contentType = response.headers.get("content-type") || "image/png";
const arrayBuffer = await response.arrayBuffer();
const base64 = Buffer.from(arrayBuffer).toString("base64");

return {
type: "image_url",
image_url: {
url: `data:${contentType};base64,${base64}`,
},
};
} catch (error) {
logger.warn("Error converting image to base64", {
url,
error: error instanceof Error ? error.message : String(error),
});
return image;
}
}),
);
}

Copilot AI Jan 14, 2026

Copy link

Choose a reason for hiding this comment

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

This extracted function lacks dedicated test coverage. Consider adding unit tests to verify error handling (failed fetch, network errors), data URL detection, and successful base64 conversion.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +12
import type { ModelDefinition } from "@llmgateway/models";

/**
* Checks if a model is truly free (has free flag AND no per-request pricing)
*/
export function isModelTrulyFree(modelInfo: ModelDefinition): boolean {
if (!modelInfo.free) {
return false;
}
// Check if any provider has a per-request cost
return !modelInfo.providers.some((p) => p.requestPrice && p.requestPrice > 0);
}

Copilot AI Jan 14, 2026

Copy link

Choose a reason for hiding this comment

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

This extracted function lacks dedicated test coverage. Consider adding unit tests to verify behavior when models have the free flag but have per-request pricing, when models don't have the free flag, and when models are truly free.

Copilot uses AI. Check for mistakes.
@steebchen steebchen changed the title refactor(chat): modularize schemas refactor(chat): modularize chat handler with incremental modules Jan 14, 2026
steebchen and others added 3 commits January 23, 2026 02:23
…del utils

- Move completions request schema to separate completions.ts file for clarity
- Extract convertImagesToBase64 utility to dedicated tool file
- Extract estimateTokensFromContent to separate tool
- Extract isModelTrulyFree utility for model pricing checks
- Remove duplicates and redundant code from chat.ts to improve maintainability

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
…d update imports

Removed the re-export of estimateTokensFromContent from the chat module to clean up legacy code. Updated tests to import estimateTokensFromContent directly from the tools directory for clarity and maintainability.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@steebchen
steebchen force-pushed the terragon/refactor-chat-handler-incremental-9nl3n9 branch from c2291e9 to f042781 Compare January 24, 2026 00:11
@steebchen steebchen closed this Jan 29, 2026
@steebchen
steebchen deleted the terragon/refactor-chat-handler-incremental-9nl3n9 branch January 29, 2026 17:43
@steebchen

Copy link
Copy Markdown
Member Author

superseded by #1537

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