-
Notifications
You must be signed in to change notification settings - Fork 220
report bundle generation #91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Reviewer's GuideThis PR implements a full report bundle generation workflow by introducing a backend ReportGenerator with new Flask endpoints, integrating cross-tab state persistence and real-time synchronization in the frontend, and adding a one-click ZIP generator UI with detailed analytics and demo/test coverage. Sequence diagram for report bundle generation and downloadsequenceDiagram
actor User
participant Frontend
participant Backend
participant ReportGenerator
User->>Frontend: Click 'Generate Report Bundle'
Frontend->>Backend: POST /api/gallery-data (sync images)
Backend->>ReportGenerator: Store gallery data
Frontend->>Backend: POST /api/reports/generate
Backend->>ReportGenerator: create_report_bundle()
ReportGenerator->>Backend: Return report metadata
Backend->>Frontend: Respond with report info
User->>Frontend: Click 'Download Report Bundle'
Frontend->>Backend: GET /api/reports/download/<filename>
Backend->>Frontend: Send ZIP file
Frontend->>Backend: POST /api/gallery-data (clear data)
Backend->>ReportGenerator: Clear gallery data
Frontend->>User: Show download and reset UI
Class diagram for new and updated gallery stores and ReportGeneratorclassDiagram
class ReportGenerator {
+fetch_gallery_data()
+create_csv_records()
+write_csv()
+generate_config_json()
+generate_readme()
+copy_images_to_bundle()
+create_report_bundle()
+_validate_csv_paths_in_zip()
}
class ImageRecord {
+id: str
+filename: str
+relative_path: str
+prompt: str
+negative_prompt: str
+model_name: str
+sampler_name: str
+steps: int
+cfg_scale: float
+width: int
+height: int
+seed: int
+timestamp: str
+generation_type: str
+batch_index: int
+denoising_strength: float
+input_image_path: str
+lora_models: str
+controlnet_info: str
+file_size_bytes: int
+get_required_columns()
+validate_csv_schema(csv_path)
}
class useTxt2ImgGalleryStore {
+images: ImageResult[]
+addImages(newImages)
+clearImages()
+removeImage(id)
+setLoading(loading)
}
class useImg2ImgGalleryStore {
+images: ImageResult[]
+addImages(newImages)
+clearImages()
+removeImage(id)
+setLoading(loading)
}
class useExtrasGalleryStore {
+images: ImageResult[]
+addImages(newImages)
+clearImages()
+removeImage(id)
+setLoading(loading)
}
ReportGenerator --> ImageRecord
useTxt2ImgGalleryStore --|> ImageResult
useImg2ImgGalleryStore --|> ImageResult
useExtrasGalleryStore --|> ImageResult
Class diagram for GallerySync utilityclassDiagram
class GallerySync {
+syncToBackend()
+syncFromBackend()
+addImageAndSync(type, images)
+clearAll()
+ensureFreshStart()
}
GallerySync --> useTxt2ImgGalleryStore
GallerySync --> useImg2ImgGalleryStore
GallerySync --> useExtrasGalleryStore
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughAdds end-to-end report generation and gallery sync: backend report generator and API endpoints, frontend sync utilities and UI (Reports tab + ReportGenerator), persistent gallery stores, multiple test/demo scripts, and startup logic to reset gallery data. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Frontend
participant GallerySync
participant BackendAPI
participant ReportGenerator
Frontend->>GallerySync: addImageAndSync(type, images)
GallerySync->>BackendAPI: POST /api/gallery-data
BackendAPI-->>GallerySync: 200 OK
Frontend->>BackendAPI: POST /api/reports/generate {filename?}
BackendAPI->>ReportGenerator: create_report_bundle(gallery_data)
ReportGenerator-->>BackendAPI: {report_path, stats, validations}
BackendAPI-->>Frontend: {status, report info}
Frontend->>BackendAPI: GET /api/reports/download/<filename>
BackendAPI-->>Frontend: ZIP file
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @patel-ayushi - I've reviewed your changes and they look great!
Blocking issues:
- function
health_check
is defined inside a function but never used (link) - function
update_gallery_data
is defined inside a function but never used (link) - function
generate_report
is defined inside a function but never used (link) - function
validate_csv_schema
is defined inside a function but never used (link) - time.sleep() call; did you mean to leave this in? (link)
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `dream_layer_backend/dream_layer.py:625` </location>
<code_context>
+ "message": "No data provided"
+ }), 400
+
+ # Store gallery data temporarily for report generation
+ # In a production system, this would be stored in a database
+ gallery_file = os.path.join(os.path.dirname(__file__), 'temp_gallery_data.json')
+ with open(gallery_file, 'w', encoding='utf-8') as f:
+ json.dump(data, f, indent=2, ensure_ascii=False)
+
</code_context>
<issue_to_address>
Storing gallery data in a file may lead to concurrency issues.
Without file locking or atomic writes, simultaneous requests may cause data corruption. Please implement file locks or consider a more reliable storage solution for concurrent access.
</issue_to_address>
### Comment 2
<location> `dream_layer_backend/txt2img_workflow.py:40` </location>
<code_context>
height = max(64, min(2048, int(data.get('height', 512))))
- batch_size = max(1, min(8, int(data.get('batch_size', 1))))
- steps = max(1, min(150, int(data.get('steps', 20))))
+ batch_size = 1 # Force batch_size to 1 for faster generation
+ steps = min(15, max(1, int(data.get('steps', 15)))) # Max 15 steps for speed
cfg_scale = max(1.0, min(20.0, float(data.get('cfg_scale', 7.0))))
denoising_strength = max(
</code_context>
<issue_to_address>
Forcing batch_size and steps may reduce flexibility for users.
If these constraints are intended for a specific mode, make them configurable or document the restriction to prevent user confusion.
</issue_to_address>
### Comment 3
<location> `dream_layer_backend/img2img_workflow.py:77` </location>
<code_context>
height = max(64, min(2048, int(data.get('height', 512))))
- batch_size = max(1, min(8, int(data.get('batch_size', 1))))
- steps = max(1, min(150, int(data.get('steps', 20))))
+ batch_size = 1 # Force batch_size to 1 for faster generation
+ steps = min(15, max(1, int(data.get('steps', 15)))) # Max 15 steps for speed
cfg_scale = max(1.0, min(20.0, float(data.get('cfg_scale', 7.0))))
denoising_strength = max(
0.0, min(1.0, float(data.get('denoising_strength', 0.75))))
input_image = data.get('input_image', '')
- model_name = data.get('model_name', 'v1-6-pruned-emaonly-fp16.safetensors')
+ model_name = "v15PrunedEmaonly_v15PrunedEmaonly.safetensors" # Force fast model
+ print(f"Forcing model: {model_name} for faster generation")
sampler_name = data.get('sampler_name', 'euler')
scheduler = data.get('scheduler', 'normal')
</code_context>
<issue_to_address>
Forcing batch_size and model_name reduces user configurability.
Please consider allowing users to configure batch_size and model_name, or make the forced values opt-in, to avoid unexpected behavior.
</issue_to_address>
### Comment 4
<location> `dream_layer_frontend/src/utils/gallerySync.ts:122` </location>
<code_context>
+ }
+
+ // Wait a bit for state to update, then sync to backend
+ setTimeout(async () => {
+ await this.syncToBackend();
+ }, 100);
+ }
+
</code_context>
<issue_to_address>
Using setTimeout for state synchronization may introduce race conditions.
Using a fixed delay does not guarantee the state is updated before syncing. Instead, trigger the backend sync in response to the actual state change, such as via a callback or subscription.
Suggested implementation:
```typescript
}
// Subscribe to gallery store changes and sync to backend
switch (galleryType) {
case 'txt2img':
useTxt2ImgGalleryStore.subscribe(() => {
this.syncToBackend();
});
break;
case 'img2img':
useImg2ImgGalleryStore.subscribe(() => {
this.syncToBackend();
});
break;
case 'extras':
useExtrasGalleryStore.subscribe(() => {
this.syncToBackend();
});
break;
}
}
```
- If the subscription should only be registered once (not every time this function runs), consider moving the subscription logic to an initialization function or constructor.
- If you want to sync only on specific state changes (e.g., when images are added), you may need to compare previous and next state in the subscription callback.
- Ensure that `useTxt2ImgGalleryStore`, `useImg2ImgGalleryStore`, and `useExtrasGalleryStore` are Zustand stores and expose a `.subscribe` method.
</issue_to_address>
## Security Issues
### Issue 1
<location> `dream_layer_backend/test_api_standalone.py:30` </location>
<issue_to_address>
**security (python.lang.maintainability.useless-inner-function):** function `health_check` is defined inside a function but never used
*Source: opengrep*
</issue_to_address>
### Issue 2
<location> `dream_layer_backend/test_api_standalone.py:34` </location>
<issue_to_address>
**security (python.lang.maintainability.useless-inner-function):** function `update_gallery_data` is defined inside a function but never used
*Source: opengrep*
</issue_to_address>
### Issue 3
<location> `dream_layer_backend/test_api_standalone.py:60` </location>
<issue_to_address>
**security (python.lang.maintainability.useless-inner-function):** function `generate_report` is defined inside a function but never used
*Source: opengrep*
</issue_to_address>
### Issue 4
<location> `dream_layer_backend/test_api_standalone.py:97` </location>
<issue_to_address>
**security (python.lang.maintainability.useless-inner-function):** function `validate_csv_schema` is defined inside a function but never used
*Source: opengrep*
</issue_to_address>
### Issue 5
<location> `dream_layer_backend/test_api_standalone.py:144` </location>
<issue_to_address>
**security (python.lang.best-practice.arbitrary-sleep):** time.sleep() call; did you mean to leave this in?
*Source: opengrep*
</issue_to_address>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 39
🔭 Outside diff range comments (2)
dream_layer_frontend/src/utils/imageTransfer.ts (1)
2-13
: OptimizetransferImages
lookup and confirmid
presence
- Verified that
ImageResult
insrc/types/generationSettings.ts
has anid: string
.- To reduce the filter’s complexity from O(n·m) to O(n + m) when
ids
is large, convertids
to aSet
before filtering.File:
src/utils/imageTransfer.ts
export const transferImages = ( srcStore: { images: ImageResult[] }, dstStore: { addImages: (images: ImageResult[]) => void }, ids: string[] ) => { - const imagesToTransfer = srcStore.images.filter(img => ids.includes(img.id)); + const idSet = new Set(ids); + const imagesToTransfer = srcStore.images.filter(img => idSet.has(img.id)); if (imagesToTransfer.length > 0) { dstStore.addImages(imagesToTransfer); } };dream_layer_frontend/src/features/Img2Img/Img2ImgPage.tsx (1)
190-205
: Wrap awaited sync in try/finally inside onload; otherwise errors are uncaught and UI may not resetErrors thrown in
testImage.onload = async () => { await ... }
won’t be caught by the outer try/catch. Ensure loading state resets even if sync fails.-testImage.onload = async () => { - console.log('Test image loaded successfully:', firstImageUrl); - const images = data.comfy_response.generated_images.map((img: any) => ({ - id: `${Date.now()}-${Math.random()}`, - url: img.url, - prompt: requestData.prompt, - negativePrompt: requestData.negative_prompt, - timestamp: Date.now(), - settings: requestData - })); - - console.log('Adding images to store:', images); - - // Use centralized sync to add images and sync to backend - await GallerySync.addImageAndSync('img2img', images); - - setLoading(false); - setIsGenerating(false); -}; +testImage.onload = async () => { + console.log('Test image loaded successfully:', firstImageUrl); + const images = data.comfy_response.generated_images.map((img: any) => ({ + id: `${Date.now()}-${Math.random()}`, + url: img.url, + prompt: requestData.prompt, + negativePrompt: requestData.negative_prompt, + timestamp: Date.now(), + settings: requestData + })); + console.log('Adding images to store:', images); + try { + await GallerySync.addImageAndSync('img2img', images); + } catch (err) { + console.error('Failed to sync generated images to backend:', err); + } finally { + setLoading(false); + setIsGenerating(false); + } +};
🧹 Nitpick comments (36)
dream_layer_backend/create_test_images.py (4)
8-8
: Remove unused import
shutil
is imported but never used.-import shutil
29-31
: Backward-compatibility for Pillow versions lacking textbbox (optional)Some environments may run older Pillow versions without
textbbox
. Provide a small fallback totextsize
to avoid crashes.- bbox = draw.textbbox((0, 0), text, font=font) - text_width = bbox[2] - bbox[0] - text_height = bbox[3] - bbox[1] + try: + bbox = draw.textbbox((0, 0), text, font=font) + text_width = bbox[2] - bbox[0] + text_height = bbox[3] - bbox[1] + except AttributeError: + # Fallback for older Pillow + text_width, text_height = draw.textsize(text, font=font)
15-17
: Deterministic test assets (optional)For reproducible tests, consider seeding
random
or deriving colors deterministically fromfilename
/text
.- bg_color = random.choice(colors) + # For reproducibility in tests, seed before calling or derive a stable color + bg_color = random.choice(colors)
171-171
: Remove extraneous f-stringNo placeholders; plain string is cleaner.
- print(f"📄 Gallery data saved to: temp_gallery_data.json") + print("📄 Gallery data saved to: temp_gallery_data.json")dream_layer_backend/temp_gallery_data.json (1)
1-5
: Seed structure LGTM; avoid committing runtime mutationsThe empty arrays for txt2img/img2img/extras match the backend’s expected shape.
Operational tip:
- Ensure this file is treated as a seed. Consider guarding against accidental commits of runtime mutations (e.g., add a pre-commit hook or mark as assume-unchanged).
- Medium-term: file-based shared state can suffer from concurrent writes; an in-memory cache with periodic flush, or a lightweight DB (SQLite) with atomic writes would be more robust. Happy to draft a migration plan if useful.
dream_layer_frontend/src/components/Navigation/TabsNav.tsx (1)
1-1
: Minor nit: add a space after the comma for readability
HardDrive,FolderArchive
→HardDrive, FolderArchive
-import { FileText, ImageIcon, Settings, GalleryHorizontal, HardDrive,FolderArchive, History, Download, MessageSquare } from "lucide-react"; +import { FileText, ImageIcon, Settings, GalleryHorizontal, HardDrive, FolderArchive, History, Download, MessageSquare } from "lucide-react";Also verify your
lucide-react
version includesFolderArchive
; if not, pick a supported icon.dream_layer_frontend/src/features/Txt2Img/Txt2ImgPage.tsx (2)
19-23
: Drop unused store imports unless you plan to observe them
useImg2ImgGalleryStore
anduseExtrasGalleryStore
are only used to select images below and those selections aren’t consumed elsewhere. Remove the imports to avoid unnecessary subscriptions/re-renders, or add an effect to react to changes if that was the intention.-import { useImg2ImgGalleryStore } from '@/stores/useImg2ImgGalleryStore'; -import { useExtrasGalleryStore } from '@/stores/useExtrasGalleryStore'; +// (remove if not observing other stores here) import { Txt2ImgCoreSettings, defaultTxt2ImgSettings } from '@/types/generationSettings'; import { GallerySync } from '@/utils/gallerySync';
44-46
: Remove unused selectors or wire them into an autosync effect
txt2imgImages
,img2imgImages
, andextrasImages
aren’t read later. Keep only what’s used, or add an effect to auto-sync when any gallery changes.-const txt2imgImages = useTxt2ImgGalleryStore(state => state.images); -const img2imgImages = useImg2ImgGalleryStore(state => state.images); -const extrasImages = useExtrasGalleryStore(state => state.images); +// Remove unless used for a watcher effectOptional watcher pattern:
useEffect(() => { const id = setTimeout(() => GallerySync.syncToBackend(), 200); return () => clearTimeout(id); }, [useTxt2ImgGalleryStore.getState().images]); // or derive a stable selector patterndream_layer_frontend/src/features/Img2Img/Img2ImgPage.tsx (2)
15-17
: Remove unused cross-store imports unless you will observe themThese imports exist only to select images that aren’t used later. Removing them will reduce needless subscriptions and re-renders.
-import { useTxt2ImgGalleryStore } from '@/stores/useTxt2ImgGalleryStore'; -import { useExtrasGalleryStore } from '@/stores/useExtrasGalleryStore'; +// (remove if not observing other stores here)
66-69
: Drop unused image selectors or add a watcher effectThese selections aren’t used later; either remove them or add an autosync effect triggered by gallery changes.
-const txt2imgImages = useTxt2ImgGalleryStore(state => state.images); -const img2imgImages = useImg2ImgGalleryStore(state => state.images); -const extrasImages = useExtrasGalleryStore(state => state.images); +// Remove unless you plan to observe and react to cross-store changes heredream_layer_frontend/src/stores/useExtrasGalleryStore.ts (2)
14-34
: Align clearImages/isLoading semantics across storesHere clearImages resets isLoading to false, while img2img preserves it. Pick one convention across stores to reduce surprising behavior during cross-tab sync.
14-34
: DRY opportunity: extract a tiny gallery store factoryTxt2Img and Extras stores are near-identical. Consider a helper like createSimpleGalleryStore(nameKey) to reduce duplication and ensure future changes propagate consistently.
dream_layer_frontend/src/stores/useImg2ImgGalleryStore.ts (1)
43-46
: Align clearImages/isLoading behavior with other storesThis store preserves isLoading in clearImages, while txt2img/extras reset it to false. Align for consistent UX, or document the difference.
- clearImages: () => set((state) => ({ - images: [], - isLoading: state.isLoading // Preserve the current loading state - })), + clearImages: () => set({ images: [], isLoading: false }),dream_layer_backend/update_gallery_with_real_images.py (2)
6-6
: Remove unused importjson is imported but never used.
-import json
121-124
: Fix f-string without placeholdersRemove the unnecessary f prefix.
- print(f"📊 Created gallery data:") + print("📊 Created gallery data:")dream_layer_frontend/src/components/ReportGenerator.tsx (2)
161-168
: Avoid orphaned timers; clear timeouts on unmountThe setTimeout calls re-fetch without cleanup. Store handles and clear them in the return cleanup to prevent stray updates after unmount or rapid tab switches.
useEffect(() => { - if (frontendTotalImages > 0 && frontendTotalImages !== backendImageCount) { - console.log(`🔄 Frontend count (${frontendTotalImages}) differs from backend (${backendImageCount}), refreshing...`); - setTimeout(() => { - fetchBackendImageCount(true); // Skip loading state for refresh - }, 1000); // Give time for sync to complete - } + let t: number | undefined; + if (frontendTotalImages > 0 && frontendTotalImages !== backendImageCount) { + console.log(`🔄 Frontend count (${frontendTotalImages}) differs from backend (${backendImageCount}), refreshing...`); + t = window.setTimeout(() => { + fetchBackendImageCount(true); + }, 1000); + } + return () => { if (t) clearTimeout(t); }; }, [frontendTotalImages, backendImageCount]); useEffect(() => { - if (txt2imgImages.length > 0 || img2imgImages.length > 0 || extrasImages.length > 0) { - console.log(`🔄 Store change detected: txt2img=${txt2imgImages.length}, img2img=${img2imgImages.length}, extras=${extrasImages.length}`); - setTimeout(() => { - fetchBackendImageCount(true); - }, 1500); // Slightly longer delay for cross-tab scenarios - } + let t: number | undefined; + if (txt2imgImages.length > 0 || img2imgImages.length > 0 || extrasImages.length > 0) { + console.log(`🔄 Store change detected: txt2img=${txt2imgImages.length}, img2img=${img2imgImages.length}, extras=${extrasImages.length}`); + t = window.setTimeout(() => { + fetchBackendImageCount(true); + }, 1500); + } + return () => { if (t) clearTimeout(t); }; }, [txt2imgImages.length, img2imgImages.length, extrasImages.length]);Also applies to: 171-178
269-293
: Clear-all silently ignores backend errors
GallerySync.clearAll()
posts to clear backend but doesn't check response.ok. Consider checking and surfacing a warning to the user if backend clear fails, to avoid UI-backend drift.dream_layer_backend/test_frontend_sync.py (2)
7-7
: Remove unused import
json
isn't used in this script.-import json
17-23
: Stabilize generated IDs and timestamp formatUsing a float timestamp in IDs can introduce
.
characters; also prefer ISO strings for timestamps to match backend expectations elsewhere.- "id": f"frontend_sim_{datetime.now().timestamp()}", + "id": f"frontend_sim_{int(datetime.now().timestamp())}", ... - "timestamp": datetime.now(timezone.utc).isoformat(), + "timestamp": datetime.now(timezone.utc).isoformat(),Note: If backend expects
negative_prompt
, align the field name.Is
negativePrompt
accepted by the backend schema, or should we sendnegative_prompt
? I can update this test accordingly.dream_layer_frontend/src/utils/gallerySync.ts (2)
34-41
: Add fetch timeouts with AbortController for robustnessIn browsers, fetch has no built-in timeout. Add AbortController to avoid hanging UIs when backend is down.
const withTimeout = async <T>(p: Promise<T>, ms = 10000): Promise<T> => { const ctrl = new AbortController(); const id = setTimeout(() => ctrl.abort(), ms); try { // @ts-ignore return await p(ctrl.signal); } finally { clearTimeout(id); } }; // Usage: const post = (url: string, body: any) => withTimeout(fetch(url, { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify(body), signal: (new AbortController()).signal })); // Apply similarly to GETs.Alternatively, wrap each fetch with a per-call AbortController and setTimeout.
Also applies to: 60-63, 137-147, 159-162
9-9
: Consider env-based configuration for BACKEND_URLHardcoding localhost is brittle. Prefer Vite/Next env vars and default fallback.
- private static readonly BACKEND_URL = 'http://localhost:5002'; + private static readonly BACKEND_URL = + (import.meta as any).env?.VITE_BACKEND_URL ?? 'http://localhost:5002';debug_frontend_state.html (2)
36-41
: Unify timestamp format and include filename to better match backend schemaUse ISO timestamp (string) and include a filename field. This aligns with other tests and improves downstream consistency.
id: `debug_test_${Date.now()}`, - url: "http://localhost:5001/api/images/test.png", + filename: "test.png", + url: "http://localhost:5001/api/images/test.png", prompt: "debug test image", negativePrompt: "blurry", - timestamp: Date.now(), + timestamp: new Date().toISOString(),
63-66
: Guard JSON parsing on non-2xx responsesIf the server returns HTML/error,
response.json()
will throw. Checkok
first.- const result = await response.json(); - log(`✅ Gallery sync result: ${JSON.stringify(result)}`); + if (response.ok) { + const result = await response.json(); + log(`✅ Gallery sync result: ${JSON.stringify(result)}`); + } else { + const text = await response.text(); + log(`❌ Gallery sync failed: ${response.status} ${text}`); + }complete_workflow_test.py (2)
8-8
: Remove unused importtimezone
timezone
is not used.-from datetime import datetime, timezone +from datetime import datetime
20-37
: Consider settingfilename
explicitly to avoid relying on URL parsing
create_csv_records
falls back to extracting filename from URL; providingfilename
is more robust.Would you like me to patch this to include a
filename
field derived from the URL for determinism?dream_layer_backend/dream_layer.py (1)
738-766
: Align with PR objective: auto-clear session after downloadPR states session data auto-clears after report download. This endpoint doesn’t clear
temp_gallery_data.json
. Consider clearing after response usingafter_this_request
.Example pattern:
from flask import after_this_request @after_this_request def _clear_session(response): try: gallery_file = os.path.join(os.path.dirname(__file__), 'temp_gallery_data.json') if os.path.exists(gallery_file): os.remove(gallery_file) except Exception: pass return responseWould you like me to wire this in and update frontend to re-sync after clear?
dream_layer_backend/test_report_system.py (3)
7-20
: Trim unused imports to keep tests cleanRemove unused: json, time, threading, Path, Dict, Any, patch, MagicMock.
-import json import tempfile import shutil import zipfile import csv -import time import requests -import threading -from pathlib import Path -from typing import Dict, Any import unittest -from unittest.mock import patch, MagicMock
329-334
: Use a singlewith
for multiple context managersSimplifies nested contexts and satisfies linter SIM117.
- with zipfile.ZipFile(result['report_path'], 'r') as zipf: - with zipf.open('results.csv') as csv_file: - with open(csv_path, 'wb') as f: - f.write(csv_file.read()) + with zipfile.ZipFile(result['report_path'], 'r') as zipf, \ + zipf.open('results.csv') as csv_file, \ + open(csv_path, 'wb') as f: + f.write(csv_file.read())
507-509
: Remove f-prefix on constant stringMinor style fix flagged by linter (F541).
- print(f"✅ Manual test completed successfully!") + print("✅ Manual test completed successfully!")dream_layer_backend/report_generator.py (4)
8-8
: Remove unused importPath
Not used in this module.
-from pathlib import Path
395-401
: Rename unuseddirs
loop var to_dirs
Minor cleanup per linter B007.
- with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zipf: - for root, dirs, files in os.walk(temp_dir): + with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zipf: + for root, _dirs, files in os.walk(temp_dir):
317-348
: Optional: Include input images in bundle when presentYou populate
input_image_path
for img2img but don’t copy input images. Consider decoding base64settings['input_image']
and writing togrids/input_images/
to improve bundle completeness. This requires passing original gallery data or enriching records; happy to draft a design.Shall I propose an approach that preserves provenance with minimal CSV bloat?
75-84
: Coupling and side effects cautionCalling
get_directories()
in__init__
pulls settings and touches the filesystem. That’s fine operationally but can surprise consumers (tests/demos). Consider lazy evaluation or injecting paths for testability. The tests already subclass to override paths; documenting this would help.dream_layer_backend/demo_report_workflow.py (3)
13-15
: Remove unused imports
datetime
andPath
are unused.-from datetime import datetime -from pathlib import Path
337-341
: Combine nestedwith
statementsCleaner and satisfies SIM117.
- with zipfile.ZipFile(result['report_path'], 'r') as zipf: - with zipf.open('results.csv') as csv_file: - with open(csv_path, 'wb') as f: - f.write(csv_file.read()) + with zipfile.ZipFile(result['report_path'], 'r') as zipf, \ + zipf.open('results.csv') as csv_file, \ + open(csv_path, 'wb') as f: + f.write(csv_file.read())
229-233
: Removef
prefix from constant stringsSeveral
f
for clarity and to satisfy F541.I can run an autofix pass (Ruff/Black) against this file if desired.
Also applies to: 241-246, 266-271, 278-281, 297-301, 303-306, 307-317, 311-317, 319-324, 363-366, 371-381, 383-383
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
dream_layer_backend/reports/cross_tab_accumulation_test.zip
is excluded by!**/*.zip
dream_layer_backend/reports/cross_tab_demo_complete.zip
is excluded by!**/*.zip
dream_layer_backend/reports/dreamlayer_report_20250810T040213.zip
is excluded by!**/*.zip
dream_layer_backend/reports/dreamlayer_report_20250810T041217.zip
is excluded by!**/*.zip
📒 Files selected for processing (25)
complete_workflow_test.py
(1 hunks)debug_frontend_state.html
(1 hunks)dream_layer_backend/create_test_images.py
(1 hunks)dream_layer_backend/demo_report_workflow.py
(1 hunks)dream_layer_backend/dream_layer.py
(1 hunks)dream_layer_backend/img2img_workflow.py
(1 hunks)dream_layer_backend/report_generator.py
(1 hunks)dream_layer_backend/temp_gallery_data.json
(1 hunks)dream_layer_backend/test_api_standalone.py
(1 hunks)dream_layer_backend/test_frontend_sync.py
(1 hunks)dream_layer_backend/test_report_system.py
(1 hunks)dream_layer_backend/txt2img_workflow.py
(2 hunks)dream_layer_backend/update_gallery_with_real_images.py
(1 hunks)dream_layer_frontend/src/components/Navigation/TabsNav.tsx
(1 hunks)dream_layer_frontend/src/components/ReportGenerator.tsx
(1 hunks)dream_layer_frontend/src/features/Extras/ExtrasPage.tsx
(3 hunks)dream_layer_frontend/src/features/Img2Img/Img2ImgPage.tsx
(4 hunks)dream_layer_frontend/src/features/Txt2Img/Txt2ImgPage.tsx
(3 hunks)dream_layer_frontend/src/pages/Index.tsx
(2 hunks)dream_layer_frontend/src/stores/useExtrasGalleryStore.ts
(1 hunks)dream_layer_frontend/src/stores/useImg2ImgGalleryStore.ts
(3 hunks)dream_layer_frontend/src/stores/useTxt2ImgGalleryStore.ts
(2 hunks)dream_layer_frontend/src/utils/gallerySync.ts
(1 hunks)dream_layer_frontend/src/utils/imageTransfer.ts
(1 hunks)start_dream_layer.sh
(1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: divyaprakash0426
PR: DreamLayer-AI/DreamLayer#40
File: docker/Dockerfile.backend.dev:4-6
Timestamp: 2025-07-16T18:40:41.273Z
Learning: The DreamLayer project follows an iterative approach to Docker development, where basic Docker setup is established first, and missing dependencies (like PyYAML) are addressed in subsequent iterations when related services (like ComfyUI) are added to the Docker files.
📚 Learning: 2025-07-16T18:40:41.273Z
Learnt from: divyaprakash0426
PR: DreamLayer-AI/DreamLayer#40
File: docker/Dockerfile.backend.dev:4-6
Timestamp: 2025-07-16T18:40:41.273Z
Learning: The DreamLayer project follows an iterative approach to Docker development, where basic Docker setup is established first, and missing dependencies (like PyYAML) are addressed in subsequent iterations when related services (like ComfyUI) are added to the Docker files.
Applied to files:
start_dream_layer.sh
📚 Learning: 2025-07-16T05:27:07.946Z
Learnt from: divyaprakash0426
PR: DreamLayer-AI/DreamLayer#34
File: ComfyUI/custom_nodes/luma_photon_node/luma_photon_node.py:224-224
Timestamp: 2025-07-16T05:27:07.946Z
Learning: In the LumaPhotonDepth2Img node (ComfyUI/custom_nodes/luma_photon_node/luma_photon_node.py), the weight transformation `1.0 - image_weight` is intentional and functions as designed, despite the tooltip indicating higher values should preserve the image more.
Applied to files:
dream_layer_backend/txt2img_workflow.py
dream_layer_backend/img2img_workflow.py
📚 Learning: 2025-07-14T22:55:51.063Z
Learnt from: rockerBOO
PR: DreamLayer-AI/DreamLayer#28
File: dream_layer_frontend/src/components/WorkflowCustomNode.tsx:88-106
Timestamp: 2025-07-14T22:55:51.063Z
Learning: In the DreamLayer frontend codebase, the team prefers to rely on TypeScript's type system for data validation rather than adding defensive programming checks, when the types are well-defined and data flow is controlled.
Applied to files:
debug_frontend_state.html
📚 Learning: 2025-08-06T18:59:31.412Z
Learnt from: rachanavarsha
PR: DreamLayer-AI/DreamLayer#80
File: report_bundler/bundler.py:7-7
Timestamp: 2025-08-06T18:59:31.412Z
Learning: In report_bundler/bundler.py, the REQUIRED_COLUMNS set intentionally contains only essential fields (image_path, sampler, steps, cfg, preset, seed) needed for bundling logic. Additional columns like width, height, grid_label, and notes are treated as optional metadata.
Applied to files:
dream_layer_backend/report_generator.py
🧬 Code Graph Analysis (7)
start_dream_layer.sh (1)
start_dream_layer_linux.sh (1)
print_success
(22-24)
dream_layer_frontend/src/pages/Index.tsx (2)
dream_layer_backend/report_generator.py (1)
ReportGenerator
(75-456)dream_layer_frontend/src/components/ReportGenerator.tsx (1)
ReportGenerator
(36-491)
dream_layer_frontend/src/features/Txt2Img/Txt2ImgPage.tsx (4)
dream_layer_frontend/src/stores/useTxt2ImgGalleryStore.ts (1)
useTxt2ImgGalleryStore
(14-34)dream_layer_frontend/src/stores/useImg2ImgGalleryStore.ts (1)
useImg2ImgGalleryStore
(32-128)dream_layer_frontend/src/stores/useExtrasGalleryStore.ts (1)
useExtrasGalleryStore
(14-34)dream_layer_frontend/src/utils/gallerySync.ts (1)
GallerySync
(8-185)
dream_layer_frontend/src/features/Extras/ExtrasPage.tsx (4)
dream_layer_frontend/src/stores/useExtrasGalleryStore.ts (1)
useExtrasGalleryStore
(14-34)dream_layer_frontend/src/stores/useTxt2ImgGalleryStore.ts (1)
useTxt2ImgGalleryStore
(14-34)dream_layer_frontend/src/stores/useImg2ImgGalleryStore.ts (1)
useImg2ImgGalleryStore
(32-128)dream_layer_frontend/src/utils/gallerySync.ts (1)
GallerySync
(8-185)
dream_layer_backend/update_gallery_with_real_images.py (1)
dream_layer_backend/create_test_images.py (1)
main
(44-174)
dream_layer_frontend/src/utils/gallerySync.ts (3)
dream_layer_frontend/src/stores/useTxt2ImgGalleryStore.ts (1)
useTxt2ImgGalleryStore
(14-34)dream_layer_frontend/src/stores/useImg2ImgGalleryStore.ts (1)
useImg2ImgGalleryStore
(32-128)dream_layer_frontend/src/stores/useExtrasGalleryStore.ts (1)
useExtrasGalleryStore
(14-34)
dream_layer_backend/dream_layer.py (4)
dream_layer_backend/test_api_standalone.py (3)
update_gallery_data
(35-58)generate_report
(61-95)validate_csv_schema
(98-122)dream_layer_backend/report_generator.py (5)
ReportGenerator
(75-456)fetch_gallery_data
(85-100)create_report_bundle
(350-429)validate_csv_schema
(48-73)ImageRecord
(15-73)dream_layer_frontend/src/components/ReportGenerator.tsx (1)
ReportGenerator
(36-491)dream_layer_backend/demo_report_workflow.py (1)
fetch_gallery_data
(183-184)
🪛 Ruff (0.12.2)
dream_layer_backend/create_test_images.py
8-8: shutil
imported but unused
Remove unused import: shutil
(F401)
25-25: Do not use bare except
(E722)
171-171: f-string without any placeholders
Remove extraneous f
prefix
(F541)
dream_layer_backend/img2img_workflow.py
80-80: Local variable denoising_strength
is assigned to but never used
Remove assignment to unused variable denoising_strength
(F841)
dream_layer_backend/update_gallery_with_real_images.py
6-6: json
imported but unused
Remove unused import: json
(F401)
121-121: f-string without any placeholders
Remove extraneous f
prefix
(F541)
dream_layer_backend/test_frontend_sync.py
7-7: json
imported but unused
Remove unused import: json
(F401)
complete_workflow_test.py
8-8: datetime.timezone
imported but unused
Remove unused import: datetime.timezone
(F401)
dream_layer_backend/test_api_standalone.py
11-11: shutil
imported but unused
Remove unused import: shutil
(F401)
15-15: flask.Flask
imported but unused
Remove unused import: flask.Flask
(F401)
23-23: Redefinition of unused Flask
from line 15
Remove definition: Flask
(F811)
150-150: Do not use bare except
(E722)
dream_layer_backend/demo_report_workflow.py
13-13: datetime.datetime
imported but unused
Remove unused import: datetime.datetime
(F401)
14-14: pathlib.Path
imported but unused
Remove unused import: pathlib.Path
(F401)
229-229: f-string without any placeholders
Remove extraneous f
prefix
(F541)
241-241: f-string without any placeholders
Remove extraneous f
prefix
(F541)
266-266: f-string without any placeholders
Remove extraneous f
prefix
(F541)
278-278: f-string without any placeholders
Remove extraneous f
prefix
(F541)
297-297: f-string without any placeholders
Remove extraneous f
prefix
(F541)
303-303: f-string without any placeholders
Remove extraneous f
prefix
(F541)
307-307: f-string without any placeholders
Remove extraneous f
prefix
(F541)
311-311: f-string without any placeholders
Remove extraneous f
prefix
(F541)
319-319: f-string without any placeholders
Remove extraneous f
prefix
(F541)
335-335: f-string without any placeholders
Remove extraneous f
prefix
(F541)
337-338: Use a single with
statement with multiple contexts instead of nested with
statements
(SIM117)
363-363: f-string without any placeholders
Remove extraneous f
prefix
(F541)
365-365: f-string without any placeholders
Remove extraneous f
prefix
(F541)
372-372: f-string without any placeholders
Remove extraneous f
prefix
(F541)
374-374: f-string without any placeholders
Remove extraneous f
prefix
(F541)
375-375: f-string without any placeholders
Remove extraneous f
prefix
(F541)
376-376: f-string without any placeholders
Remove extraneous f
prefix
(F541)
377-377: f-string without any placeholders
Remove extraneous f
prefix
(F541)
378-378: f-string without any placeholders
Remove extraneous f
prefix
(F541)
379-379: f-string without any placeholders
Remove extraneous f
prefix
(F541)
381-381: f-string without any placeholders
Remove extraneous f
prefix
(F541)
383-383: f-string without any placeholders
Remove extraneous f
prefix
(F541)
dream_layer_backend/test_report_system.py
8-8: json
imported but unused
Remove unused import: json
(F401)
13-13: time
imported but unused
Remove unused import: time
(F401)
15-15: threading
imported but unused
Remove unused import: threading
(F401)
16-16: pathlib.Path
imported but unused
Remove unused import: pathlib.Path
(F401)
17-17: typing.Dict
imported but unused
Remove unused import
(F401)
17-17: typing.Any
imported but unused
Remove unused import
(F401)
19-19: unittest.mock.patch
imported but unused
Remove unused import
(F401)
19-19: unittest.mock.MagicMock
imported but unused
Remove unused import
(F401)
329-330: Use a single with
statement with multiple contexts instead of nested with
statements
(SIM117)
350-350: Do not use bare except
(E722)
507-507: f-string without any placeholders
Remove extraneous f
prefix
(F541)
dream_layer_backend/report_generator.py
8-8: pathlib.Path
imported but unused
Remove unused import: pathlib.Path
(F401)
253-256: Use ternary operator checkpoints = response.json() if response.status_code == 200 else []
instead of if
-else
-block
Replace if
-else
-block with checkpoints = response.json() if response.status_code == 200 else []
(SIM108)
257-257: Do not use bare except
(E722)
396-396: Loop control variable dirs
not used within loop body
Rename unused dirs
to _dirs
(B007)
🔇 Additional comments (13)
dream_layer_backend/create_test_images.py (1)
72-73
: Verify image URLs match the serving backendEnsure these URLs map to where your backend serves static images. In this PR, servers run on multiple ports; confirm that
/api/images/*
on the referenced ports actually servesdream_layer_backend/served_images/*
.Would you like a quick grep script to locate the image-serving endpoints and confirm the correct port/path?
Also applies to: 90-91, 108-109, 128-129, 147-148
start_dream_layer.sh (1)
179-181
: Verified fresh-start schema sync & atomic write recommendationConfirmed that the reset JSON
{"txt2img": [], "img2img": [], "extras": []}matches the backend’s expected keys (seen in
dream_layer.py
,report_generator.py
,temp_gallery_data.json
, etc.).• For safer writes, consider atomically replacing the file:
tmp=$(mktemp) printf '%s\n' '{"txt2img": [], "img2img": [], "extras": []}' > "$tmp" && mv "$tmp" dream_layer_backend/temp_gallery_data.json• No other startup scripts (e.g.
start_dream_layer_linux.sh
) were found—if you add one, mirror this reset there as well.dream_layer_frontend/src/pages/Index.tsx (1)
14-14
: Import looks good
ReportGenerator
integration via direct import is fine.dream_layer_frontend/src/features/Txt2Img/Txt2ImgPage.tsx (1)
255-259
: Centralized add + backend sync: LGTMUsing
GallerySync.addImageAndSync('txt2img', images)
keeps stores and backend consistent.dream_layer_frontend/src/features/Img2Img/Img2ImgPage.tsx (1)
18-18
: GallerySync integration import: LGTMGood to centralize synchronization logic behind a utility.
dream_layer_frontend/src/stores/useTxt2ImgGalleryStore.ts (1)
14-34
: Good use of Zustand persist with selective statePersisting only images via partialize is correct; avoiding isLoading persistence prevents stale UI states. The prepend semantics in addImages are consistent with gallery UX.
dream_layer_frontend/src/stores/useExtrasGalleryStore.ts (1)
14-34
: LGTM; mirrors txt2img store with correct selective persistenceImplementation is straightforward and correct. Name key is unique; partialize excludes transient isLoading as intended.
dream_layer_frontend/src/features/Extras/ExtrasPage.tsx (2)
42-51
: Backend sync on mount looks goodCalling GallerySync.syncFromBackend() on mount is a sensible default to hydrate stores from backend.
171-190
: ImageResult.timestamp is correctly typed as a numberThe
ImageResult
interface in bothsrc/types/imageResult.ts
andsrc/types/generationSettings.ts
declarestimestamp: number
, and you’re assigningDate.now()
(which returns a number). No change is required here.dream_layer_frontend/src/stores/useImg2ImgGalleryStore.ts (1)
32-128
: Selective persistence is implemented correctlyPersisting images and coreSettings (excluding File) aligns with the workflow and avoids serializing non-serializable state.
dream_layer_backend/test_api_standalone.py (1)
229-233
: Good: explicit timeouts on report generation requestNice use of a 30s timeout for a potentially heavy operation.
dream_layer_backend/dream_layer.py (1)
614-635
: Confirm impact of strict gallery payload validationI attempted to locate other callers of
/api/gallery-data
via Ripgrep, but the search failed to yield any matches. Before enforcing the schema below, please manually verify that all existing clients of this endpoint will includetxt2img
andimg2img
as arrays:- data = request.json + data = request.json if not data: return jsonify({ "status": "error", "message": "No data provided" }), 400 + # Minimal schema check + if not isinstance(data, dict) or not all(k in data for k in ('txt2img', 'img2img')): + return jsonify({"status": "error", "message": "Invalid gallery schema"}), 400 + for k in ('txt2img', 'img2img', 'extras'): + if k in data and not isinstance(data[k], list): + return jsonify({"status": "error", "message": f"'{k}' must be a list"}), 400dream_layer_backend/report_generator.py (1)
11-13
: Makeget_directories
import robust and avoid circular/package issuesUnqualified
from dream_layer import get_directories
is fragile. Provide package-qualified import with fallback.-from dream_layer import get_directories +try: + # When running inside the package + from dream_layer_backend.dream_layer import get_directories # type: ignore +except Exception: + # Fallback for scripts/tests run from this folder + from dream_layer import get_directories # type: ignore⛔ Skipped due to learnings
Learnt from: divyaprakash0426 PR: DreamLayer-AI/DreamLayer#40 File: docker/Dockerfile.backend.dev:4-6 Timestamp: 2025-07-16T18:40:41.273Z Learning: The DreamLayer project follows an iterative approach to Docker development, where basic Docker setup is established first, and missing dependencies (like PyYAML) are addressed in subsequent iterations when related services (like ComfyUI) are added to the Docker files.
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
dream_layer_frontend/src/features/Txt2Img/Txt2ImgPage.tsx (1)
314-324
: Keep batch/WebSocket images in backend sync too.Use GallerySync in the image_generated handler to persist images server-side as they stream in.
- newSocket.on('image_generated', (data) => { + newSocket.on('image_generated', async (data) => { const newImage = { id: `${Date.now()}-${Math.random()}`, url: data.image_data.url, prompt: data.prompt, negativePrompt: coreSettings.negative_prompt, timestamp: Date.now(), settings: { ...coreSettings } }; - addImages([newImage]); + await GallerySync.addImageAndSync('txt2img', [newImage]); });
♻️ Duplicate comments (12)
dream_layer_backend/img2img_workflow.py (1)
77-84
: Don’t silently force batch/steps/model; gate via env flag, use logger, and wire denoiseOverride only when “fast” preset is enabled; otherwise honor user inputs. Replace print with logger and use the already-parsed denoising_strength to fix Ruff F841.
Apply:
- batch_size = 1 # Force batch_size to 1 for faster generation - steps = min(15, max(1, int(data.get('steps', 15)))) # Max 15 steps for speed + force_fast = os.getenv('DL_FORCE_FAST_PRESET', '1') == '1' + max_steps = int(os.getenv('DL_MAX_STEPS', '15')) + batch_size = 1 if force_fast else max(1, int(data.get('batch_size', 1))) + steps = (min(max_steps, max(1, int(data.get('steps', max_steps)))) + if force_fast else max(1, int(data.get('steps', 15))))- model_name = "v15PrunedEmaonly_v15PrunedEmaonly.safetensors" # Force fast model - print(f"Forcing model: {model_name} for faster generation") + requested = data.get('model_name') or data.get('ckpt_name') + default_fast = os.getenv('DL_FAST_MODEL', 'v15PrunedEmaonly_v15PrunedEmaonly.safetensors') + model_name = default_fast if force_fast else (requested or default_fast) + logger.info("Using model: %s (%s)", model_name, "forced fast" if force_fast else "requested/fallback")And in core settings (outside this hunk):
- 'denoise': 1.0, + 'denoise': denoising_strength,dream_layer_backend/txt2img_workflow.py (1)
49-55
: Make fast preset configurable; don’t override user inputs by defaultHonor client-provided batch_size/steps unless an env-gated fast mode is on.
- # Batch parameters with validation (from smallFeatures) - LIMITED TO 1 FOR PERFORMANCE - batch_size = 1 # Force batch_size to 1 for faster generation - print(f"\nBatch size: {batch_size} (forced to 1 for performance)") - - # Sampling parameters with validation - LIMITED STEPS FOR FASTER GENERATION - steps = min(15, max(1, int(data.get('steps', 15)))) # Max 15 steps for speed + force_fast = os.getenv('DL_FORCE_FAST_PRESET', '1') == '1' + max_steps = int(os.getenv('DL_MAX_STEPS', '15')) + if force_fast: + batch_size = 1 + steps = min(max_steps, max(1, int(data.get('steps', max_steps)))) + print(f"\nBatch size: {batch_size} (fast preset); steps: {steps} (max {max_steps})") + else: + batch_size = max(1, int(data.get('batch_size', 1))) + steps = max(1, int(data.get('steps', 15)))dream_layer_backend/dream_layer.py (6)
622-648
: Concurrent file writes: use atomic write to avoid corruptionReplace direct write with atomic rename via a temp file.
- gallery_file = os.path.join(os.path.dirname(__file__), 'temp_gallery_data.json') - with open(gallery_file, 'w', encoding='utf-8') as f: - json.dump(data, f, indent=2, ensure_ascii=False) + gallery_file = os.path.join(os.path.dirname(__file__), 'temp_gallery_data.json') + import tempfile + dirpath = os.path.dirname(gallery_file) + with tempfile.NamedTemporaryFile('w', delete=False, dir=dirpath, encoding='utf-8') as tmp: + json.dump(data, tmp, indent=2, ensure_ascii=False) + tmp_path = tmp.name + os.replace(tmp_path, gallery_file)
649-669
: Import robustness: use package-qualified import with fallbackAvoid bare
from report_generator import ReportGenerator
to prevent package import failures.- # Import here to avoid circular imports - from report_generator import ReportGenerator + # Import here to avoid circular imports + try: + from dream_layer_backend.report_generator import ReportGenerator + except ImportError: + from report_generator import ReportGenerator
670-707
: Same import robustness for status endpointMirror the qualified import + fallback.
- # Import here to avoid circular imports - from report_generator import ReportGenerator + # Import here to avoid circular imports + try: + from dream_layer_backend.report_generator import ReportGenerator + except ImportError: + from report_generator import ReportGenerator
708-721
: Path traversal via filename; sanitize before useValidate and sanitize
filename
before passing to the generator.- data = request.json or {} - output_filename = data.get('filename') + data = request.json or {} + output_filename = data.get('filename') + if output_filename: + if any(sep in output_filename for sep in ('..', '/', '\\')): + return jsonify({"status": "error", "message": "Invalid filename"}), 400 + output_filename = os.path.basename(output_filename) + if not output_filename.lower().endswith('.zip'): + output_filename += '.zip'
746-780
: Harden download: validate first and safe-join inside reports dirUse safe_join and check before touching the path.
- reports_dir = os.path.join(os.path.dirname(__file__), 'reports') - report_path = os.path.join(reports_dir, filename) - - if not os.path.exists(report_path): - return jsonify({ - "status": "error", - "message": "Report file not found" - }), 404 - - # Security check: ensure filename doesn't contain path traversal - if '..' in filename or '/' in filename or '\\' in filename: - return jsonify({ - "status": "error", - "message": "Invalid filename" - }), 400 + reports_dir = os.path.join(os.path.dirname(__file__), 'reports') + # Security check before filesystem access + if any(sep in filename for sep in ('..', '/', '\\')): + return jsonify({"status": "error", "message": "Invalid filename"}), 400 + from werkzeug.utils import safe_join + report_path = safe_join(reports_dir, filename) + if not report_path or not os.path.exists(report_path): + return jsonify({"status": "error", "message": "Report file not found"}), 404
781-807
: Arbitrary file read in CSV validation; restrict to reports dirOnly allow CSVs within the reports directory.
- data = request.json + data = request.json if not data or 'csv_path' not in data: return jsonify({ "status": "error", "message": "CSV path not provided" }), 400 - - csv_path = data['csv_path'] + reports_dir = os.path.join(os.path.dirname(__file__), 'reports') + requested = os.path.basename(data['csv_path']) + from werkzeug.utils import safe_join + csv_path = safe_join(reports_dir, requested) + if not csv_path or not os.path.exists(csv_path): + return jsonify({"status": "error", "message": "CSV not found in reports directory"}), 404dream_layer_frontend/src/features/Img2Img/Img2ImgPage.tsx (1)
70-78
: Run fresh-start check before first sync and handle errors (mirrors prior guidance).Wrap initial sync in try/catch and call ensureFreshStart first.
// Load existing data on component mount useEffect(() => { const loadExistingData = async () => { - console.log('📥 Img2Img: Loading existing gallery data from backend...'); - await GallerySync.syncFromBackend(); + try { + console.log('📥 Img2Img: Ensuring fresh start and loading gallery data from backend...'); + await GallerySync.ensureFreshStart(); + await GallerySync.syncFromBackend(); + } catch (e) { + console.error('Failed to sync galleries on mount (img2img):', e); + } }; loadExistingData(); }, []);dream_layer_frontend/src/features/Txt2Img/Txt2ImgPage.tsx (1)
59-67
: Run fresh-start check before first sync and handle errors (as previously requested).// Load existing data on component mount useEffect(() => { const loadExistingData = async () => { - console.log('📥 Txt2Img: Loading existing gallery data from backend...'); - await GallerySync.syncFromBackend(); + try { + console.log('📥 Txt2Img: Ensuring fresh start and loading gallery data...'); + await GallerySync.ensureFreshStart(); + await GallerySync.syncFromBackend(); + } catch (e) { + console.error('Failed to sync galleries on mount (txt2img):', e); + } }; loadExistingData(); }, []);dream_layer_frontend/src/features/Extras/ExtrasPage.tsx (2)
39-41
: Remove unused selectors to avoid re-renders.These are unused and subscribe to foreign stores.
- const txt2imgImages = useTxt2ImgGalleryStore(state => state.images); - const img2imgImages = useImg2ImgGalleryStore(state => state.images);
231-247
: Fix condition typo and ensure integer dimensions; strengthen IDs.Use 'resize-to' (matching UI), round dimensions, and add a short random suffix to reduce collisions.
- id: `extras_${Date.now()}`, + id: `extras_${Date.now()}_${Math.random().toString(36).slice(2,8)}`, @@ - width: upscaleMethod === 'upscale-to' ? resizeWidth : 512 * upscaleFactor, - height: upscaleMethod === 'upscale-to' ? resizeHeight : 512 * upscaleFactor, + width: upscaleMethod === 'resize-to' ? Math.round(resizeWidth) : Math.round(512 * upscaleFactor), + height: upscaleMethod === 'resize-to' ? Math.round(resizeHeight) : Math.round(512 * upscaleFactor),
🧹 Nitpick comments (7)
dream_layer_frontend/src/features/Img2Img/Img2ImgPage.tsx (3)
66-68
: Remove unused store selectors to avoid unnecessary re-renders.These subscriptions trigger re-renders on unrelated updates and aren't used.
- const txt2imgImages = useTxt2ImgGalleryStore(state => state.images); - const img2imgImages = useImg2ImgGalleryStore(state => state.images); - const extrasImages = useExtrasGalleryStore(state => state.images);
15-16
: If selectors are removed, drop unused imports too.-import { useTxt2ImgGalleryStore } from '@/stores/useTxt2ImgGalleryStore'; -import { useExtrasGalleryStore } from '@/stores/useExtrasGalleryStore';
138-141
: Remove unused FormData creation.You send JSON; this FormData is dead code and misleading.
- const formData = new FormData(); - formData.append('image', inputImage.file); -dream_layer_frontend/src/features/Txt2Img/Txt2ImgPage.tsx (2)
52-54
: Remove unused store selectors to prevent unrelated re-renders.These values are never read in this component.
- const txt2imgImages = useTxt2ImgGalleryStore(state => state.images); - const img2imgImages = useImg2ImgGalleryStore(state => state.images); - const extrasImages = useExtrasGalleryStore(state => state.images);
20-21
: Drop unused imports if removing the selectors.-import { useImg2ImgGalleryStore } from '@/stores/useImg2ImgGalleryStore'; -import { useExtrasGalleryStore } from '@/stores/useExtrasGalleryStore'; @@ -import { GallerySync } from '@/utils/gallerySync'; +import { GallerySync } from '@/utils/gallerySync';Also applies to: 23-24
dream_layer_frontend/src/features/Extras/ExtrasPage.tsx (2)
23-27
: If selectors are removed, drop now-unused imports.-import { useTxt2ImgGalleryStore } from '@/stores/useTxt2ImgGalleryStore'; -import { useImg2ImgGalleryStore } from '@/stores/useImg2ImgGalleryStore'; import { ImageResult } from '@/types/generationSettings'; import { GallerySync } from '@/utils/gallerySync';
42-50
: Add fresh-start check and error handling on mount.Match other tabs’ startup behavior for consistent persistence semantics.
// Load existing data on component mount useEffect(() => { const loadExistingData = async () => { - console.log('📥 Extras: Loading existing gallery data from backend...'); - await GallerySync.syncFromBackend(); + try { + console.log('📥 Extras: Ensuring fresh start and loading gallery data...'); + await GallerySync.ensureFreshStart(); + await GallerySync.syncFromBackend(); + } catch (e) { + console.error('Failed to sync galleries on mount (extras):', e); + } }; loadExistingData(); }, []);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
dream_layer_backend/dream_layer.py
(1 hunks)dream_layer_backend/img2img_workflow.py
(1 hunks)dream_layer_backend/txt2img_workflow.py
(2 hunks)dream_layer_frontend/src/components/Navigation/TabsNav.tsx
(1 hunks)dream_layer_frontend/src/features/Extras/ExtrasPage.tsx
(3 hunks)dream_layer_frontend/src/features/Img2Img/Img2ImgPage.tsx
(4 hunks)dream_layer_frontend/src/features/Txt2Img/Txt2ImgPage.tsx
(3 hunks)start_dream_layer.sh
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- start_dream_layer.sh
- dream_layer_frontend/src/components/Navigation/TabsNav.tsx
🧰 Additional context used
🧬 Code graph analysis (4)
dream_layer_frontend/src/features/Extras/ExtrasPage.tsx (4)
dream_layer_frontend/src/stores/useExtrasGalleryStore.ts (1)
useExtrasGalleryStore
(14-34)dream_layer_frontend/src/stores/useTxt2ImgGalleryStore.ts (1)
useTxt2ImgGalleryStore
(14-34)dream_layer_frontend/src/stores/useImg2ImgGalleryStore.ts (1)
useImg2ImgGalleryStore
(32-128)dream_layer_frontend/src/utils/gallerySync.ts (1)
GallerySync
(8-185)
dream_layer_frontend/src/features/Txt2Img/Txt2ImgPage.tsx (4)
dream_layer_frontend/src/stores/useTxt2ImgGalleryStore.ts (1)
useTxt2ImgGalleryStore
(14-34)dream_layer_frontend/src/stores/useImg2ImgGalleryStore.ts (1)
useImg2ImgGalleryStore
(32-128)dream_layer_frontend/src/stores/useExtrasGalleryStore.ts (1)
useExtrasGalleryStore
(14-34)dream_layer_frontend/src/utils/gallerySync.ts (1)
GallerySync
(8-185)
dream_layer_frontend/src/features/Img2Img/Img2ImgPage.tsx (4)
dream_layer_frontend/src/stores/useTxt2ImgGalleryStore.ts (1)
useTxt2ImgGalleryStore
(14-34)dream_layer_frontend/src/stores/useImg2ImgGalleryStore.ts (1)
useImg2ImgGalleryStore
(32-128)dream_layer_frontend/src/stores/useExtrasGalleryStore.ts (1)
useExtrasGalleryStore
(14-34)dream_layer_frontend/src/utils/gallerySync.ts (1)
GallerySync
(8-185)
dream_layer_backend/dream_layer.py (2)
dream_layer_backend/test_api_standalone.py (3)
update_gallery_data
(35-58)generate_report
(61-95)validate_csv_schema
(98-122)dream_layer_backend/report_generator.py (5)
ReportGenerator
(75-456)fetch_gallery_data
(85-100)create_report_bundle
(350-429)validate_csv_schema
(48-73)ImageRecord
(15-73)
🪛 Ruff (0.12.2)
dream_layer_backend/dream_layer.py
643-643: Do not catch blind exception: Exception
(BLE001)
646-646: Use explicit conversion flag
Replace with conversion flag
(RUF010)
661-661: Do not catch blind exception: Exception
(BLE001)
664-664: Use explicit conversion flag
Replace with conversion flag
(RUF010)
702-702: Do not catch blind exception: Exception
(BLE001)
705-705: Use explicit conversion flag
Replace with conversion flag
(RUF010)
740-740: Do not catch blind exception: Exception
(BLE001)
743-743: Use explicit conversion flag
Replace with conversion flag
(RUF010)
775-775: Do not catch blind exception: Exception
(BLE001)
778-778: Use explicit conversion flag
Replace with conversion flag
(RUF010)
802-802: Do not catch blind exception: Exception
(BLE001)
805-805: Use explicit conversion flag
Replace with conversion flag
(RUF010)
dream_layer_backend/txt2img_workflow.py
76-76: SyntaxError: Expected except
or finally
after try
block
dream_layer_backend/img2img_workflow.py
80-80: Local variable denoising_strength
is assigned to but never used
Remove assignment to unused variable denoising_strength
(F841)
🔇 Additional comments (3)
dream_layer_frontend/src/features/Img2Img/Img2ImgPage.tsx (2)
203-205
: Good use of centralized sync.addImageAndSync keeps frontend and backend in step. LGTM.
18-18
: Typo concern resolved
Search confirms every reference usesuseImg2ImgGalleryStore
(no lowercase “i” instances found).Likely an incorrect or invalid review comment.
dream_layer_frontend/src/features/Txt2Img/Txt2ImgPage.tsx (1)
263-267
: LGTM: centralized add + sync for single-generation flow.
# Update the data with the actual seed used | ||
data['seed'] = seed | ||
|
||
# Handle model name validation | ||
# Handle model name validation | ||
if data.get("force_fast_model", False): | ||
model_name = "v15PrunedEmaonly_v15PrunedEmaonly.safetensors" # Force fast model | ||
print(f"Forcing model: {model_name} for faster generation") | ||
else: | ||
model_name = data.get('model_name', 'juggernautXL_v8Rundiffusion.safetensors') | ||
|
||
# Check if it's a closed-source model (DALL-E, FLUX, Ideogram, Runway, Stability AI, Luma, Banana, etc.) | ||
closed_source_models = ['dall-e-3', 'dall-e-2', 'flux-pro', 'flux-dev', 'ideogram-v3', 'runway-gen4', 'stability-sdxl', 'stability-sd-turbo', 'photon-1', 'photon-flash-1', 'banana-gemini'] | ||
|
||
# Check if it's a closed-source model (DALL-E, FLUX, Ideogram, Runway, Stability AI, Luma, Banana, etc.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix syntax error: lines are outside the surrounding try block
Lines 75–76 and 78–85 are at the same indent as the outer try:
(Line 35), causing “Expected except/finally after try”. Indent them into the try-suite.
- # Update the data with the actual seed used
- data['seed'] = seed
+ # Update the data with the actual seed used
+ data['seed'] = seed
- # Handle model name validation
- if data.get("force_fast_model", False):
- model_name = "v15PrunedEmaonly_v15PrunedEmaonly.safetensors" # Force fast model
- print(f"Forcing model: {model_name} for faster generation")
- else:
- model_name = data.get('model_name', 'juggernautXL_v8Rundiffusion.safetensors')
+ # Handle model selection (env-gated fast preset)
+ force_fast = os.getenv('DL_FORCE_FAST_PRESET', '1') == '1'
+ requested = data.get('model_name') or data.get('ckpt_name')
+ default_fast = os.getenv('DL_FAST_MODEL', 'v15PrunedEmaonly_v15PrunedEmaonly.safetensors')
+ model_name = default_fast if force_fast else (requested or 'juggernautXL_v8Rundiffusion.safetensors')
+ print(f"Using model: {model_name} ({'forced fast' if force_fast else 'requested/fallback'})")
📝 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.
# Update the data with the actual seed used | |
data['seed'] = seed | |
# Handle model name validation | |
# Handle model name validation | |
if data.get("force_fast_model", False): | |
model_name = "v15PrunedEmaonly_v15PrunedEmaonly.safetensors" # Force fast model | |
print(f"Forcing model: {model_name} for faster generation") | |
else: | |
model_name = data.get('model_name', 'juggernautXL_v8Rundiffusion.safetensors') | |
# Check if it's a closed-source model (DALL-E, FLUX, Ideogram, Runway, Stability AI, Luma, Banana, etc.) | |
closed_source_models = ['dall-e-3', 'dall-e-2', 'flux-pro', 'flux-dev', 'ideogram-v3', 'runway-gen4', 'stability-sdxl', 'stability-sd-turbo', 'photon-1', 'photon-flash-1', 'banana-gemini'] | |
# Check if it's a closed-source model (DALL-E, FLUX, Ideogram, Runway, Stability AI, Luma, Banana, etc.) | |
try: | |
# … earlier code … | |
# Update the data with the actual seed used | |
data['seed'] = seed | |
# Handle model selection (env-gated fast preset) | |
force_fast = os.getenv('DL_FORCE_FAST_PRESET', '1') == '1' | |
requested = data.get('model_name') or data.get('ckpt_name') | |
default_fast = os.getenv( | |
'DL_FAST_MODEL', | |
'v15PrunedEmaonly_v15PrunedEmaonly.safetensors' | |
) | |
model_name = default_fast if force_fast else (requested or 'juggernautXL_v8Rundiffusion.safetensors') | |
print(f"Using model: {model_name} ({'forced fast' if force_fast else 'requested/fallback'})") | |
# Check if it's a closed-source model (DALL-E, FLUX, Ideogram, Runway, Stability AI, Luma, Banana, etc.) | |
… | |
except Exception as e: | |
… |
🧰 Tools
🪛 Ruff (0.12.2)
76-76: SyntaxError: Expected except
or finally
after try
block
🤖 Prompt for AI Agents
In dream_layer_backend/txt2img_workflow.py around lines 75–76 and 78–85, the
block that updates data['seed'] and determines model_name is currently at the
same indentation level as the outer try (starting ~line 35), causing a "Expected
except/finally after try" syntax error; move/indent these lines into the
try-suite so they are inside the try block (preserve existing logic, prints, and
comments) ensuring consistent indentation with the rest of the try body.
Description
Implemented a comprehensive report bundle generation system that creates downloadable ZIP archives containing detailed analytics and artifacts from image generation sessions across all DreamLayer tabs (Txt2Img, Img2Img, Extras).
Key Features: One-click ZIP generation with results.csv, config.json, organized image grids, and documentation. Cross-tab data persistence using Zustand with localStorage ensures data accumulates across tab switches and page refreshes. Real-time dashboard displays total images, individual counts by type, and dynamic generation badges (Txt2Img, Img2Img, Extras, or Multiple). Fresh start mechanism clears old data on service restart, while session automatically clears after report download. Includes comprehensive backend validation, frontend synchronization, and deterministic file paths for consistent reporting workflow.
Changes Made
Backend Changes
report_generator.py
- Core report generation logic with CSV creation, image collection, and ZIP bundlingdream_layer.py
- Added new API endpoints:/api/gallery-data
(GET/POST),/api/reports/generate
,/api/reports/download
,/api/reports/status
start_dream_layer.sh
- Added persistent data clearing on restart for fresh startsFrontend Changes
ReportGenerator.tsx
- Main reporting UI with real-time status, cross-tab data display, and download functionalitygallerySync.ts
- Centralized sync utility for backend-frontend data synchronization and fresh start detectionpersist
middleware touseTxt2ImgGalleryStore.ts
,useImg2ImgGalleryStore.ts
, and createduseExtrasGalleryStore.ts
Txt2ImgPage.tsx
,Img2ImgPage.tsx
, andExtrasPage.tsx
to auto-sync data to backend after image generationData Flow Changes
Evidence Required ✅
UI Screenshot
Logs