Skip to content

feat: implement AI settings and widgets creation with streaming response#1809

Merged
Artuomka merged 1 commit into
mainfrom
backend_ai_settings_response
May 28, 2026
Merged

feat: implement AI settings and widgets creation with streaming response#1809
Artuomka merged 1 commit into
mainfrom
backend_ai_settings_response

Conversation

@Artuomka
Copy link
Copy Markdown
Collaborator

@Artuomka Artuomka commented May 28, 2026

Summary by CodeRabbit

  • New Features
    • Added real-time progress streaming for AI settings and widgets creation. Users can now see live updates as the system discovers tables, inspects them, generates AI configurations, and creates widgets through the /ai/v2/setup/:connectionId endpoint.

Review Change Stack

Copilot AI review requested due to automatic review settings May 28, 2026 10:25
@Artuomka Artuomka enabled auto-merge May 28, 2026 10:26
Copy link
Copy Markdown
Contributor

Copilot AI left a 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 changes AI setup from a queued request into a streaming progress endpoint for creating AI-generated table settings and widgets.

Changes:

  • Adds streaming response handling for GET /ai/v2/setup/:connectionId.
  • Adds progress messages during AI scan/settings/widget creation.
  • Adds utility and tests for emitted settings messages plus e2e coverage for the stream.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
backend/src/entities/ai/use-cases/request-ai-settings-and-widgets-creation.use.case.ts Streams AI setup progress and terminal chunks.
backend/src/entities/ai/user-ai-requests-v2.controller.ts Passes Express response into the AI setup use case.
backend/src/entities/ai/application/data-structures/request-ai-settings-creation.ds.ts Adds request DS carrying connection data and response.
backend/src/entities/ai/ai-use-cases.interface.ts Updates interface to use the new DS.
backend/src/entities/shared-jobs/shared-jobs.service.ts Adds progress callbacks/messages and rethrows scan errors.
backend/src/entities/shared-jobs/utils/emit-settings-messages.util.ts Adds helper for per-setting progress messages.
backend/test/ava-tests/non-saas-tests/* Adds unit and e2e tests for progress message emission and streaming setup.

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

}
} catch (error) {
console.error('Error during AI scan and creation of settings/widgets: ', error);
Sentry.captureException(error);
widgetEntity.settings = savedSetting;
widgetsToSave.push(widgetEntity);
message(
`Added ${widget.widget_type} widget for table "${savedSetting.table_name}" on column "${widget.field_name}"`,
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

📝 Walkthrough

Walkthrough

This PR adds Server-Sent Events (SSE) streaming to the AI settings creation endpoint. The /ai/v2/setup/:connectionId endpoint now streams real-time progress updates as newline-delimited JSON chunks throughout database scanning and AI-based settings generation, replacing a silent fire-and-forget background job pattern.

Changes

AI Settings Streaming Implementation

Layer / File(s) Summary
Request data contract and interface updates
backend/src/entities/ai/application/data-structures/request-ai-settings-creation.ds.ts, backend/src/entities/ai/ai-use-cases.interface.ts
New RequestAISettingsCreationDs class extends connection data with an Express response object. IAISettingsAndWidgetsCreation interface updated to accept this new data structure instead of plain connection data.
Use case streaming and SSE implementation
backend/src/entities/ai/use-cases/request-ai-settings-and-widgets-creation.use.case.ts
Use case extracts response object, sets SSE headers, orchestrates progress callbacks from the service, writes completion/error chunks with getErrorMessage() formatting, and handles failures via Sentry capture with conditional HTTP 500 sending based on header send state.
Controller response injection and payload construction
backend/src/entities/ai/user-ai-requests-v2.controller.ts
Controller injects Express response via @Res({ passthrough: true }), updates Swagger docs to describe newline-delimited JSON streaming, and passes response through use case using new RequestAISettingsCreationDs payload.
Service progress emission infrastructure and callback mechanism
backend/src/entities/shared-jobs/shared-jobs.service.ts (imports, types, method signature)
Introduces AIScanProgressChunk and AIScanProgressCallback types, updates scanDatabaseAndCreateSettingsAndWidgetsWithAI to accept optional callback, removes test-mode early-return to enable streaming during tests.
Service progress message emissions throughout scanning pipeline
backend/src/entities/shared-jobs/shared-jobs.service.ts (scan logic)
Emits messages for: table discovery and selection count, per-table successful inspection, inspection failures, "no valid tables" condition, AI generation start/completion, zero-settings outcome, validation failures with skip messaging, and per-widget creation.
Table settings message formatting utility
backend/src/entities/shared-jobs/utils/emit-settings-messages.util.ts
New emitSettingsMessages utility formats progress messages describing each table's applied settings: display name, search fields, readonly fields, column view, ordering, and identity column; emits default message when no parameters are configured.
Test environment isolation for background scans
backend/src/entities/connection/use-cases/create-connection.use.case.ts
Adds isTest() check to suppress automatic background scans during test runs, allowing tests to explicitly invoke the stream when needed without database pollution.
End-to-end streaming behavior tests
backend/test/ava-tests/non-saas-tests/non-saas-ai-settings-creation-stream-e2e.test.ts
Mocks AICoreService, verifies text/event-stream response headers, parses NDJSON chunks, asserts presence of "Starting AI scan", table names, per-parameter messages ("display_name parameter set to"), "Added widget" messaging, and validates completion and error chunks across multiple endpoint invocations.
Unit tests for settings message formatting
backend/test/ava-tests/non-saas-tests/non-saas-emit-settings-messages.test.ts
Tests emitSettingsMessages for: one message per populated parameter with correct ordering, skipping null/undefined/empty-array values, default fallback when no parameters populated, consistent table name inclusion, and array parameter joining with comma-space.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • lyubov-voloshko
  • gugu

Poem

🐰 A stream of progress flows so bright,
Where tables dance in SSE light,
Each widget sparkles, each setting sings,
While rabbits celebrate all these things! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Security Check ⚠️ Warning Response streaming lacks writable state checks violating OWASP A08. Client disconnect causes unhandled response.write/end failures. Guard all response.write/end calls with isWritable() state checks before writing, prevent writes after disconnect as flagged in review comments.
✅ Passed checks (4 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: implementing AI settings and widgets creation with streaming response support, which is reflected throughout the changeset with SSE headers, chunk writing, and progress callbacks.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch backend_ai_settings_response

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

@coderabbitai coderabbitai Bot requested review from gugu and lyubov-voloshko May 28, 2026 10:30
@Artuomka Artuomka merged commit 9393201 into main May 28, 2026
16 of 20 checks passed
@Artuomka Artuomka deleted the backend_ai_settings_response branch May 28, 2026 10:33
Copy link
Copy Markdown

@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: 1

🧹 Nitpick comments (4)
backend/src/entities/ai/use-cases/request-ai-settings-and-widgets-creation.use.case.ts (1)

63-63: ⚡ Quick win

Use a template literal for chunk serialization.

Replace string concatenation with a template literal for guideline compliance.

Proposed fix
-		response.write(JSON.stringify(chunk) + '\n');
+		response.write(`${JSON.stringify(chunk)}\n`);

As per coding guidelines, "Use template literals instead of string concatenation".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/src/entities/ai/use-cases/request-ai-settings-and-widgets-creation.use.case.ts`
at line 63, Replace the string concatenation in the streaming response call so
it uses a template literal: locate the response.write call in
request-ai-settings-and-widgets-creation.use.case.ts (the line that currently
does response.write(JSON.stringify(chunk) + '\n')) and change it to use a
template literal to append the newline to the serialized chunk (keep
JSON.stringify(chunk) as-is, only alter how the '\n' is concatenated).
backend/test/ava-tests/non-saas-tests/non-saas-emit-settings-messages.test.ts (1)

42-56: ⚡ Quick win

Consider adding test coverage for falsy but valid enum values.

Test case 2 validates that null, undefined, and empty arrays are correctly skipped. However, it doesn't verify behavior when ordering is set to a potentially falsy but valid enum value (e.g., 0 if QueryOrderingEnum uses numeric values).

If QueryOrderingEnum.ASC = 0, the utility function would skip it due to the truthiness check, but this test wouldn't catch that bug.

🧪 Suggested additional test case
test('includes ordering even when it is a falsy enum value', (t) => {
	// Only add this test if QueryOrderingEnum can have value 0 or other falsy values
	const setting = buildSetting({
		ordering: 0 as QueryOrderingEnum, // or QueryOrderingEnum.ASC if ASC = 0
	});

	const lines = collect(setting);

	t.is(lines.length, 1);
	t.true(lines[0].includes('ordering parameter set to'));
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/test/ava-tests/non-saas-tests/non-saas-emit-settings-messages.test.ts`
around lines 42 - 56, Add a test that ensures falsy-but-valid enum values (e.g.,
QueryOrderingEnum.ASC = 0) are not skipped: in the existing test file add a case
that uses buildSetting({ ordering: 0 as QueryOrderingEnum }) (or
QueryOrderingEnum.ASC) and assert collect(setting) includes an "ordering
parameter set to" line (or that lines length increases), so the collect
function's truthiness-based filter for the ordering field is validated and will
force you to adjust collect/buildSetting logic if it incorrectly treats numeric
0 as absent.
backend/src/entities/shared-jobs/utils/emit-settings-messages.util.ts (1)

18-19: emitSettingsMessages: no correctness bug for QueryOrderingEnum falsy values; optional null check for clarity

QueryOrderingEnum is a string enum (ASC / DESC), so if (setting.ordering) on backend/src/entities/shared-jobs/utils/emit-settings-messages.util.ts won’t drop valid values; null ordering is already skipped (the DB column is nullable with default null).
Optional: make the intent explicit with a null/undefined check.

Optional diff
-	if (setting.ordering) {
+	if (setting.ordering != null) {
 		params.push(['ordering', String(setting.ordering)]);
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/entities/shared-jobs/utils/emit-settings-messages.util.ts` around
lines 18 - 19, The condition currently checks falsiness for setting.ordering
which works here but is implicit; update the check in emitSettingsMessages (the
block that pushes into params for ['ordering', String(setting.ordering)]) to
explicitly test for null/undefined (e.g., setting.ordering !== null &&
setting.ordering !== undefined) so that valid enum values like 'ASC'/'DESC' are
retained and only null/undefined are skipped; keep the push to params as-is and
only change the if condition surrounding it.
backend/test/ava-tests/non-saas-tests/non-saas-ai-settings-creation-stream-e2e.test.ts (1)

33-41: ⚡ Quick win

Convert helper declarations to typed arrow constants.

These helpers currently use function declarations, and buildIterableStream also lacks an explicit return type.

♻️ Suggested update
-function buildIterableStream(chunks: Array<Record<string, unknown>>) {
-	return {
-		async *[Symbol.asyncIterator]() {
-			for (const chunk of chunks) {
-				yield chunk;
-			}
-		},
-	};
-}
+const buildIterableStream = (
+	chunks: Array<Record<string, unknown>>,
+): AsyncIterable<Record<string, unknown>> => ({
+	async *[Symbol.asyncIterator](): AsyncGenerator<Record<string, unknown>, void, unknown> {
+		for (const chunk of chunks) {
+			yield chunk;
+		}
+	},
+});

-function buildAIBatchResponse(prompt: string): string {
+const buildAIBatchResponse = (prompt: string): string => {
 	const tableNames = [...prompt.matchAll(/^Table: (\S+)/gm)].map((m) => m[1]);
 	const tables = tableNames.map((tableName) => ({
 		...
 	}));
 	return JSON.stringify({ tables });
-}
+};

-function parseNdjsonChunks(body: string): Array<Record<string, unknown>> {
+const parseNdjsonChunks = (body: string): Array<Record<string, unknown>> => {
 	return body
 		.split('\n')
 		.map((line) => line.trim())
 		.filter((line) => line.length > 0)
 		.map((line) => JSON.parse(line));
-}
+};

As per coding guidelines, "Prefer arrow functions over function declarations" and "Always add type annotations to function parameters and return types in TypeScript".

Also applies to: 43-72, 90-96

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/test/ava-tests/non-saas-tests/non-saas-ai-settings-creation-stream-e2e.test.ts`
around lines 33 - 41, Change the helper function declarations (notably
buildIterableStream) to typed const arrow functions instead of function
declarations and add explicit parameter and return types; e.g., declare
buildIterableStream as a const arrow with signature (chunks:
Array<Record<string, unknown>>) => AsyncIterable<Record<string, unknown>> (or
the appropriate AsyncIterableIterator type) and update the other helper
declarations in the same file (the other helpers called out in the review)
similarly so every helper uses arrow syntax and has explicit TypeScript
parameter and return type annotations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@backend/src/entities/ai/use-cases/request-ai-settings-and-widgets-creation.use.case.ts`:
- Around line 37-49: The try/catch must avoid writing or ending the HTTP stream
after the client disconnects: before calling this.writeChunk(...) or
response.end() (both in the happy path and inside the catch) check the response
writable state (e.g., response.writableEnded || response.finished ||
response.destroyed) and bail out early if closed; apply this guard around uses
of sharedJobsService.scanDatabaseAndCreateSettingsAndWidgetsWithAI callbacks as
well so the chunk callback returns immediately when the response is no longer
writable. Also update writeChunk to use a template literal instead of string
concatenation when serializing (replace JSON.stringify(chunk) + '\n' with a
template literal) so serialization is consistent.

---

Nitpick comments:
In
`@backend/src/entities/ai/use-cases/request-ai-settings-and-widgets-creation.use.case.ts`:
- Line 63: Replace the string concatenation in the streaming response call so it
uses a template literal: locate the response.write call in
request-ai-settings-and-widgets-creation.use.case.ts (the line that currently
does response.write(JSON.stringify(chunk) + '\n')) and change it to use a
template literal to append the newline to the serialized chunk (keep
JSON.stringify(chunk) as-is, only alter how the '\n' is concatenated).

In `@backend/src/entities/shared-jobs/utils/emit-settings-messages.util.ts`:
- Around line 18-19: The condition currently checks falsiness for
setting.ordering which works here but is implicit; update the check in
emitSettingsMessages (the block that pushes into params for ['ordering',
String(setting.ordering)]) to explicitly test for null/undefined (e.g.,
setting.ordering !== null && setting.ordering !== undefined) so that valid enum
values like 'ASC'/'DESC' are retained and only null/undefined are skipped; keep
the push to params as-is and only change the if condition surrounding it.

In
`@backend/test/ava-tests/non-saas-tests/non-saas-ai-settings-creation-stream-e2e.test.ts`:
- Around line 33-41: Change the helper function declarations (notably
buildIterableStream) to typed const arrow functions instead of function
declarations and add explicit parameter and return types; e.g., declare
buildIterableStream as a const arrow with signature (chunks:
Array<Record<string, unknown>>) => AsyncIterable<Record<string, unknown>> (or
the appropriate AsyncIterableIterator type) and update the other helper
declarations in the same file (the other helpers called out in the review)
similarly so every helper uses arrow syntax and has explicit TypeScript
parameter and return type annotations.

In
`@backend/test/ava-tests/non-saas-tests/non-saas-emit-settings-messages.test.ts`:
- Around line 42-56: Add a test that ensures falsy-but-valid enum values (e.g.,
QueryOrderingEnum.ASC = 0) are not skipped: in the existing test file add a case
that uses buildSetting({ ordering: 0 as QueryOrderingEnum }) (or
QueryOrderingEnum.ASC) and assert collect(setting) includes an "ordering
parameter set to" line (or that lines length increases), so the collect
function's truthiness-based filter for the ordering field is validated and will
force you to adjust collect/buildSetting logic if it incorrectly treats numeric
0 as absent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 62e0c6c2-a7fd-4e7c-a165-3ef66501ae66

📥 Commits

Reviewing files that changed from the base of the PR and between 6927ca0 and c560fa0.

📒 Files selected for processing (9)
  • backend/src/entities/ai/ai-use-cases.interface.ts
  • backend/src/entities/ai/application/data-structures/request-ai-settings-creation.ds.ts
  • backend/src/entities/ai/use-cases/request-ai-settings-and-widgets-creation.use.case.ts
  • backend/src/entities/ai/user-ai-requests-v2.controller.ts
  • backend/src/entities/connection/use-cases/create-connection.use.case.ts
  • backend/src/entities/shared-jobs/shared-jobs.service.ts
  • backend/src/entities/shared-jobs/utils/emit-settings-messages.util.ts
  • backend/test/ava-tests/non-saas-tests/non-saas-ai-settings-creation-stream-e2e.test.ts
  • backend/test/ava-tests/non-saas-tests/non-saas-emit-settings-messages.test.ts

Comment on lines +37 to +49
await this.sharedJobsService.scanDatabaseAndCreateSettingsAndWidgetsWithAI(connection, (chunk) =>
this.writeChunk(response, chunk),
);
this.writeChunk(response, { type: 'complete' });
response.end();
} catch (error) {
Sentry.captureException(error);
if (!response.headersSent) {
response.status(500).send({ error: 'An error occurred while processing your request.' });
return;
}
this.writeChunk(response, { type: 'error', message: getErrorMessage(error) });
response.end();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify write/end calls and absence of writable-state guards in this use case.
rg -n --type=ts -C2 'response\.write\(|response\.end\(|headersSent|writableEnded|destroyed' \
  backend/src/entities/ai/use-cases/request-ai-settings-and-widgets-creation.use.case.ts

Repository: rocket-admin/rocketadmin

Length of output: 676


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="backend/src/entities/ai/use-cases/request-ai-settings-and-widgets-creation.use.case.ts"
echo "== $FILE =="
wc -l "$FILE"
sed -n '1,140p' "$FILE" | nl -ba | sed -n '1,140p'

Repository: rocket-admin/rocketadmin

Length of output: 312


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="backend/src/entities/ai/use-cases/request-ai-settings-and-widgets-creation.use.case.ts"
echo "== $FILE (line count) =="
wc -l "$FILE"

echo "== Implementation + catch (lines ~1-80) =="
awk 'NR>=1 && NR<=80 {print NR ":" $0}' "$FILE"

echo "== writeChunk + helpers (lines ~50-120) =="
awk 'NR>=45 && NR<=120 {print NR ":" $0}' "$FILE"

Repository: rocket-admin/rocketadmin

Length of output: 3790


Guard streaming writes/ends when the client disconnects; also replace string concatenation in writeChunk.

  • implementation calls writeChunk(...) and response.end() with no writable-state checks (including in the catch path), so a mid-scan disconnect can trigger write after end-style failures and break error handling.
  • writeChunk uses string concatenation (JSON.stringify(chunk) + '\n') instead of a template literal.
Proposed fix
 	public async implementation(inputData: RequestAISettingsCreationDs): Promise<void> {
 		const { connectionId, masterPwd, response } = inputData;
@@
 		try {
 			await this.sharedJobsService.scanDatabaseAndCreateSettingsAndWidgetsWithAI(connection, (chunk) =>
 				this.writeChunk(response, chunk),
 			);
-			this.writeChunk(response, { type: 'complete' });
-			response.end();
+			if (this.isWritable(response)) {
+				this.writeChunk(response, { type: 'complete' });
+				response.end();
+			}
 		} catch (error) {
 			Sentry.captureException(error);
+			if (!this.isWritable(response)) {
+				return;
+			}
 			if (!response.headersSent) {
 				response.status(500).send({ error: 'An error occurred while processing your request.' });
 				return;
 			}
 			this.writeChunk(response, { type: 'error', message: getErrorMessage(error) });
 			response.end();
 		}
 	}
@@
 	private writeChunk(
 		response: Response,
 		chunk: { type: 'message'; text: string } | { type: 'complete' } | { type: 'error'; message: string },
 	): void {
-		response.write(JSON.stringify(chunk) + '\n');
+		if (!this.isWritable(response)) {
+			return;
+		}
+		response.write(`${JSON.stringify(chunk)}\n`);
 	}
+
+	private isWritable(response: Response): boolean {
+		return !response.writableEnded && !response.destroyed;
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await this.sharedJobsService.scanDatabaseAndCreateSettingsAndWidgetsWithAI(connection, (chunk) =>
this.writeChunk(response, chunk),
);
this.writeChunk(response, { type: 'complete' });
response.end();
} catch (error) {
Sentry.captureException(error);
if (!response.headersSent) {
response.status(500).send({ error: 'An error occurred while processing your request.' });
return;
}
this.writeChunk(response, { type: 'error', message: getErrorMessage(error) });
response.end();
await this.sharedJobsService.scanDatabaseAndCreateSettingsAndWidgetsWithAI(connection, (chunk) =>
this.writeChunk(response, chunk),
);
if (this.isWritable(response)) {
this.writeChunk(response, { type: 'complete' });
response.end();
}
} catch (error) {
Sentry.captureException(error);
if (!this.isWritable(response)) {
return;
}
if (!response.headersSent) {
response.status(500).send({ error: 'An error occurred while processing your request.' });
return;
}
this.writeChunk(response, { type: 'error', message: getErrorMessage(error) });
response.end();
}
}
private writeChunk(
response: Response,
chunk: { type: 'message'; text: string } | { type: 'complete' } | { type: 'error'; message: string },
): void {
if (!this.isWritable(response)) {
return;
}
response.write(`${JSON.stringify(chunk)}\n`);
}
private isWritable(response: Response): boolean {
return !response.writableEnded && !response.destroyed;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/src/entities/ai/use-cases/request-ai-settings-and-widgets-creation.use.case.ts`
around lines 37 - 49, The try/catch must avoid writing or ending the HTTP stream
after the client disconnects: before calling this.writeChunk(...) or
response.end() (both in the happy path and inside the catch) check the response
writable state (e.g., response.writableEnded || response.finished ||
response.destroyed) and bail out early if closed; apply this guard around uses
of sharedJobsService.scanDatabaseAndCreateSettingsAndWidgetsWithAI callbacks as
well so the chunk callback returns immediately when the response is no longer
writable. Also update writeChunk to use a template literal instead of string
concatenation when serializing (replace JSON.stringify(chunk) + '\n' with a
template literal) so serialization is consistent.

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