Skip to content

Feature/generate v4#480

Open
perinst wants to merge 8 commits intofeature/mergefrom
feature/generate-v4
Open

Feature/generate v4#480
perinst wants to merge 8 commits intofeature/mergefrom
feature/generate-v4

Conversation

@perinst
Copy link
Collaborator

@perinst perinst commented Jan 18, 2026

What changes on:

  • implement HTTP streaming for LLM content generation
  • refactor for base generate
  • remove redundant code

ISSUE:

Summary by CodeRabbit

  • New Features

    • Added a v4 HTTP streaming endpoint for content generation.
    • v3 streaming now accepts optional generation configuration (options).
  • Refactor

    • Consolidated generate routes to support v3 and v4 under a single entry.
    • Removed mindmap file-based generation functionality.
  • Behavior Change

    • Quiz generation defaults simplified to a single "MULTIPLE CHOICE" type.

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

@perinst perinst requested a review from a team January 18, 2026 15:27
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 18, 2026

Walkthrough

Adds a V4 HTTP streaming endpoint and service wiring, moves streaming logic into a base generative service, forwards an options field through V3 controller to the service, removes the Mindmap service and related provider code, consolidates generate routes, and adds prompt type mapping constants.

Changes

Cohort / File(s) Summary
Route Consolidation
src/routes/api.routes.ts, src/routes/generate/index.routes.ts, src/routes/generate/v3/generate.routes.ts
Introduces an index router mounting v3 and v4; renames v3 export to generateV3Routes; updates api.routes to import consolidated generate routes.
V4 Streaming Implementation
src/controllers/generate/v4/generate.controller.ts, src/routes/generate/v4/generate.routes.ts
New V4 controller and route: httpStreamGenerateContent streams LLM output with validation, chunked Transfer-Encoding, backpressure handling, client-disconnect cleanup, and inline error emission.
V4 Service Layer
src/services/generative/v4/generate.service.ts
New GenerateService extends BaseGenerativeService; adds streamGenerate generator delegating to streamGenerateContent; stubs for non-stream methods.
V3 Controller Update
src/controllers/generate/v3/generate.controller.ts
V3 controller now destructures and forwards optional options from request body into streamGenerateContent call.
Base Service Extension
src/services/generative/base/base.abstract.ts
Adds streamGenerateContent async generator and getResponseFormatForGenerationType helper; constructs prompts via mapping, validates type/prompt, and streams LLM chunks.
V3 Service Refactoring
src/services/generative/v3/generative.service.ts
Removes local streaming and response-format helpers (moved to base); replaces inline TYPE_PROMPT_MAPPING with imported constant reference.
Provider Cleanup
src/services/generative/llm/strategies/providers/openai/openai.service.ts
Removes Mindmap-related imports, fields, methods, and processing paths from OpenAI provider.
Constants & Utilities
src/utils/prompt/constant/prompt.constant.ts, src/services/generative/utils/constant.ts
Adds exported TYPE_PROMPT_MAPPING mapping (flashcard→FLASH_CARD, quiz→QUIZ, etc.); minor quote style change in STATUS_GEN.connected.
Feature Removal
src/services/generative/v3/mindmap.generate.service.ts
Deleted entire MindmapGenerateService (file removed: large-file handling, chunking, progress, merge logic).

Sequence Diagram

sequenceDiagram
    participant Client
    participant V4Controller as V4 Controller
    participant GenerateService as Generate Service
    participant BaseService as Base Service
    participant LLMProvider as LLM Provider

    Client->>V4Controller: POST /stream-http/llm (content, type, options)
    V4Controller->>V4Controller: validateStreamRequest()\nSet Transfer-Encoding: chunked
    V4Controller->>GenerateService: streamGenerate(payload)
    GenerateService->>BaseService: streamGenerateContent(payload)

    rect rgba(100, 150, 200, 0.5)
    Note over BaseService,LLMProvider: Main streaming loop
    BaseService->>BaseService: generatePromptText(type, content, options)
    BaseService->>BaseService: getResponseFormatForGenerationType(type)
    BaseService->>LLMProvider: request streaming with response_format

    loop For each chunk
        LLMProvider-->>BaseService: chunk data
        BaseService-->>GenerateService: yield {status, data}
        GenerateService-->>V4Controller: yield chunk
        V4Controller->>V4Controller: Check drain before write
        V4Controller-->>Client: stream chunk
    end
    end

    rect rgba(200, 100, 100, 0.5)
    Note over V4Controller,Client: Error handling & cleanup
    alt Client disconnect
        Client->>V4Controller: close/abort
        V4Controller->>V4Controller: cancel stream, cleanup listeners
    else LLM error
        LLMProvider-->>V4Controller: error
        V4Controller->>V4Controller: log and attempt inline error write
    end
    V4Controller->>V4Controller: remove listeners, end response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

feature, development

Poem

🐰 I hopped through routes and files today,
Moved streams to base, sent mindmaps away,
V4 now hums with chunked delight,
Options carried through the night,
A rabbit cheers: generation's bright! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Feature/generate v4' is vague and does not clearly communicate the main changes. While it references v4, it lacks specificity about what 'generate v4' entails. Consider a more descriptive title such as 'Implement HTTP streaming for LLM content generation' or 'Add v4 HTTP streaming controller for content generation' to better convey the primary changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@src/routes/generate/v4/generate.routes.ts`:
- Around line 10-12: The streaming LLM endpoint lacks request throttling—wrap
the authenticated route that registers
generateController.httpStreamGenerateContent with the existing
rateLimitMiddleware (same pattern used for upload routes) to protect resources;
update the router.use/auth order if needed so authMiddleware still runs and
apply rateLimitMiddleware with an appropriate window and max (e.g., short window
+ low max for heavy streaming ops) before calling
generateController.httpStreamGenerateContent to ensure authenticated requests
are rate-limited.

In `@src/services/generative/base/base.abstract.ts`:
- Around line 149-167: The code lowercases type for the prompt lookup (using
lowercase(type) into TYPE_PROMPT_MAPPING) but passes the original type to
getResponseFormatForGenerationType, causing case-sensitive mismatches (e.g.,
'SHORT_SUMMARY' won't match 'short_summary'); update streamGenerateContent so it
reuses the normalized key (the lowercase value stored in key) when calling
getResponseFormatForGenerationType (or alternatively make
getResponseFormatForGenerationType perform its own normalization), ensuring
comparisons like the 'short_summary' branch are done against a lowercased value
so response_format is selected correctly.

In `@src/utils/prompt/constant/prompt.constant.ts`:
- Around line 10-18: TYPE_PROMPT_MAPPING is missing mappings for types declared
in TYPE_PROMPT which causes undefined to be passed into generatePromptText;
either add the missing keys ('multiple_choice', 'true_false', 'fill_bank') to
TYPE_PROMPT_MAPPING mapped to their corresponding enum strings
('MULTIPLE_CHOICE', 'TRUE_FALSE', 'FILL_BANK') so lookups return valid values,
or if those types are unused remove them from the TYPE_PROMPT union; update the
mapping object in prompt.constant.ts (TYPE_PROMPT_MAPPING) and/or the
TYPE_PROMPT type so generatePromptText in base.abstract.ts always receives a
defined prompt type.
🧹 Nitpick comments (1)
src/services/generative/v3/generative.service.ts (1)

53-53: Consider using the imported constant directly.

The class field private readonly TYPE_PROMPT_MAPPING = TYPE_PROMPT_MAPPING; shadows the imported constant without modification. Unless you intend to allow subclass overrides, consider using the imported constant directly to reduce indirection.

♻️ Optional refactor

Remove line 53 and update usages at lines 210 and 567 to reference the imported TYPE_PROMPT_MAPPING directly.

@sonarqubecloud
Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/utils/prompt/quiz.prompt.ts (1)

48-48: Potential TypeError: unsafe access to listType.length.

Lines 22-26 defensively handle potentially undefined listType using optional chaining and fallbacks, but line 48 accesses listType.length directly. If listType is undefined/null, this will throw a TypeError.

Use the already-computed safe variable typeCount instead.

🐛 Proposed fix
-    - The first question type (${firstType}) should generate ${remaining} questions in total, which includes ${numberOfItem % listType.length} additional question(s) from the remainder.
+    - The first question type (${firstType}) should generate ${remaining} questions in total, which includes ${numberOfItem % typeCount} additional question(s) from the remainder.
🧹 Nitpick comments (3)
src/services/generative/base/base.abstract.ts (3)

1-1: Unused import: ResponseFormatJSONSchema.

ResponseFormatJSONSchema is imported but never used in the return values of getResponseFormatForGenerationType. Consider removing it to keep imports clean.

🧹 Suggested fix
-import { ResponseFormatJSONObject, ResponseFormatJSONSchema, ResponseFormatText } from 'openai/resources/shared';
+import { ResponseFormatJSONObject, ResponseFormatText } from 'openai/resources/shared';

168-170: Reconsider using 'connected' status for every chunk.

Yielding { status: 'connected' } for every data chunk is semantically misleading. "Connected" typically indicates an initial connection event, not ongoing data transmission. Consider using a more descriptive status like 'data' or 'chunk', or emit 'connected' only once at the start of the stream.

♻️ Suggested approach
     public async *streamGenerateContent(payload: GenerateContentRequestInterface) {
         // ... validation logic ...
 
+        yield { status: 'connected' };
+
         for await (const chunk of this.getLLMProvider().handleProcessStreamContent(prompt, { response_format })) {
-            yield { status: 'connected', data: chunk };
+            yield { status: 'data', data: chunk };
         }
     }

217-228: Simplify return type to match actual returned values.

The return type includes ResponseFormatJSONSchema but this type is never returned. Consider simplifying:

🧹 Suggested fix
     protected getResponseFormatForGenerationType(
         type: string
-    ): ResponseFormatText | ResponseFormatJSONSchema | ResponseFormatJSONObject | undefined {
+    ): ResponseFormatText | ResponseFormatJSONObject {
         switch (type) {
             case 'short_summary': {
                 return { type: 'text' };
             }
             default: {
                 return { type: 'json_object' };
             }
         }
     }

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.

1 participant