-
Notifications
You must be signed in to change notification settings - Fork 199
feat: Implement Matrix Runner for parameter sweeping #83
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: staging
Are you sure you want to change the base?
Conversation
…mentation - Complete parameter parser with range/list support - Deterministic job queue with content-addressed IDs - Pause/resume functionality with IndexedDB persistence - Integration tests for 3×2 sweep (6/6 tests passing) - Advanced features: progress visualization, smart batching, ETA calculator - Production quality: TypeScript, comprehensive testing, documentation Closes: Task DreamLayer-AI#2 Matrix Runner
Reviewer's GuideThis PR introduces a full-featured Matrix Runner for DreamLayer, enabling users to define ranges or lists of generation parameters, generate a deterministic job queue, and execute jobs sequentially with pause/resume and real-time progress tracking. The implementation uses a persisted Zustand store backed by IndexedDB, new utility functions for parsing and job orchestration, and a tabbed UI with Parameter Inputs, Progress Grid, and Job List, all covered by comprehensive integration tests. Sequence diagram for Matrix Runner job execution and pause/resumesequenceDiagram
actor User
participant MatrixRunnerStore as Store
participant IndexedDB
participant Backend as DreamLayer API
User->>Store: Clicks 'Start Matrix'
Store->>Store: Set isRunning=true, isPaused=false
loop For each job
Store->>Store: Set job.status = running
Store->>Backend: POST /api/txt2img (job parameters)
Backend-->>Store: Generation result
Store->>Store: Update job.status = completed/failed
Store->>IndexedDB: Persist state
alt User clicks Pause
User->>Store: Clicks 'Pause'
Store->>Store: Set isPaused=true, isRunning=false
Store-->>IndexedDB: Persist state
User->>Store: Clicks 'Resume'
Store->>Store: Set isPaused=false, isRunning=true
end
end
Store->>User: Update progress grid and job list
Class diagram for Matrix Runner state and job orchestrationclassDiagram
class MatrixRunnerStore {
+MatrixParameters parameters
+Txt2ImgCoreSettings baseSettings
+MatrixJob[] jobs
+int currentJobIndex
+bool isRunning
+bool isPaused
+int totalJobs
+int completedJobs
+int failedJobs
+int pendingJobs
+bool showProgressGrid
+bool autoBatch
+setParameter(param, range)
+setBaseSettings(settings)
+generateJobs()
+startMatrix()
+pauseMatrix()
+resumeMatrix()
+stopMatrix()
+clearJobs()
+executeNextJob()
+updateJobStatus(jobId, status, result, error, duration)
+toggleProgressGrid()
+toggleAutoBatch()
}
class MatrixJob {
+string id
+"pending|running|completed|failed|paused" status
+Txt2ImgCoreSettings parameters
+number createdAt
+number startedAt
+number completedAt
+string error
+result
}
class ParameterRange {
+"list|range" type
+string|number[] values
+string original
}
class MatrixParameters {
+ParameterRange seeds
+ParameterRange samplers
+ParameterRange steps
+ParameterRange cfg_scale
+ParameterRange width
+ParameterRange height
+ParameterRange batch_size
+ParameterRange batch_count
}
MatrixRunnerStore "1" o-- "*" MatrixJob : manages
MatrixRunnerStore "1" o-- "1" MatrixParameters : configures
MatrixParameters "1" o-- "*" ParameterRange : contains
MatrixJob "1" o-- "1" Txt2ImgCoreSettings : uses
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the ✨ 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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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 @iremozturk - I've reviewed your changes - here's some feedback:
- Consider refactoring the heavy matrix execution loop into a Web Worker (or background thread) to keep the UI responsive for large job batches.
- For very large matrices, the progress grid and job list components may become slow—consider adding virtualization (e.g. react-window) to optimize rendering massive lists/grids.
- The IndexedDB storage adapter is embedded in the store configuration—consider moving it to a separate module to simplify the store definition and improve testability.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Consider refactoring the heavy matrix execution loop into a Web Worker (or background thread) to keep the UI responsive for large job batches.
- For very large matrices, the progress grid and job list components may become slow—consider adding virtualization (e.g. react-window) to optimize rendering massive lists/grids.
- The IndexedDB storage adapter is embedded in the store configuration—consider moving it to a separate module to simplify the store definition and improve testability.
## Individual Comments
### Comment 1
<location> `dream_layer_frontend/src/stores/useMatrixRunnerStore.ts:13` </location>
<code_context>
+// IndexedDB storage adapter for Zustand
</code_context>
<issue_to_address>
Consider fallback or error handling for environments without IndexedDB support.
IndexedDB may not be available in all environments. Add a fallback (e.g., localStorage or memory) or provide clear error messaging if persistence fails.
Suggested implementation:
```typescript
/**
* IndexedDB storage adapter for Zustand with fallback to localStorage or in-memory storage.
*/
const createIndexedDBStorage = () => {
const dbName = 'dreamlayer-matrix-runner';
const storeName = 'matrix-runner-state';
const version = 1;
// In-memory fallback
const memoryStore: Record<string, string> = {};
// Check for IndexedDB support
const hasIndexedDB = typeof window !== 'undefined' && 'indexedDB' in window;
const hasLocalStorage = typeof window !== 'undefined' && 'localStorage' in window;
// IndexedDB methods
const indexedDBMethods = {
getItem: async (name: string): Promise<string | null> => {
return new Promise((resolve) => {
const request = indexedDB.open(dbName, version);
```
```typescript
// localStorage methods
const localStorageMethods = {
getItem: async (name: string): Promise<string | null> => {
try {
return localStorage.getItem(name);
} catch (e) {
console.warn('localStorage getItem failed', e);
return null;
}
},
setItem: async (name: string, value: string): Promise<void> => {
try {
localStorage.setItem(name, value);
} catch (e) {
console.warn('localStorage setItem failed', e);
}
},
removeItem: async (name: string): Promise<void> => {
try {
localStorage.removeItem(name);
} catch (e) {
console.warn('localStorage removeItem failed', e);
}
}
};
// In-memory methods
const memoryMethods = {
getItem: async (name: string): Promise<string | null> => {
return memoryStore[name] ?? null;
},
setItem: async (name: string, value: string): Promise<void> => {
memoryStore[name] = value;
},
removeItem: async (name: string): Promise<void> => {
delete memoryStore[name];
}
};
// Select storage backend
let storageBackend;
if (hasIndexedDB) {
storageBackend = indexedDBMethods;
} else if (hasLocalStorage) {
console.warn('IndexedDB not available, falling back to localStorage for persistence.');
storageBackend = localStorageMethods;
} else {
console.warn('Neither IndexedDB nor localStorage available, falling back to in-memory storage. Persistence will not survive reloads.');
storageBackend = memoryMethods;
}
return storageBackend;
```
</issue_to_address>
### Comment 2
<location> `dream_layer_frontend/src/stores/useMatrixRunnerStore.ts:240` </location>
<code_context>
+ // Execute the job
+ const startTime = Date.now();
+
+ const response = await fetch('http://localhost:5001/api/txt2img', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(currentJob.parameters),
+ });
+
+ if (!response.ok) {
</code_context>
<issue_to_address>
Hardcoded API endpoint may limit deployment flexibility.
Consider making the API endpoint configurable through environment variables or a global config to allow independent deployment of frontend and backend.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
const response = await fetch('http://localhost:5001/api/txt2img', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(currentJob.parameters),
});
=======
// Use environment variable for API base URL, fallback to localhost if not set
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5001';
const response = await fetch(`${API_BASE_URL}/api/txt2img`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(currentJob.parameters),
});
>>>>>>> REPLACE
</suggested_fix>
### Comment 3
<location> `dream_layer_frontend/src/stores/useMatrixRunnerStore.ts:256` </location>
<code_context>
+ const endTime = Date.now();
+ const jobDuration = (endTime - startTime) / 1000; // Convert to seconds
+
+ if (data.comfy_response?.generated_images) {
+ const images = data.comfy_response.generated_images.map((img: any) => ({
+ id: `${Date.now()}-${Math.random()}`,
+ url: img.url,
+ prompt: currentJob.parameters.prompt,
+ negativePrompt: currentJob.parameters.negative_prompt,
+ timestamp: Date.now(),
+ settings: currentJob.parameters
+ }));
+
+ // Update job with success result
</code_context>
<issue_to_address>
Assumes a specific backend response structure.
Consider adding error handling or schema validation to handle potential changes in the backend response format.
Suggested implementation:
```typescript
const data = await response.json();
const endTime = Date.now();
const jobDuration = (endTime - startTime) / 1000; // Convert to seconds
// Schema validation for backend response
// If zod is not available, replace with manual checks as needed
import { z } from "zod";
const ComfyResponseSchema = z.object({
comfy_response: z.object({
generated_images: z.array(
z.object({
url: z.string().url()
})
)
})
});
const parseResult = ComfyResponseSchema.safeParse(data);
if (!parseResult.success) {
console.error('Backend response schema validation failed:', parseResult.error);
throw new Error('Unexpected backend response format');
}
const generatedImages = parseResult.data.comfy_response.generated_images;
if (generatedImages && generatedImages.length > 0) {
const images = generatedImages.map((img: any) => ({
id: `${Date.now()}-${Math.random()}`,
url: img.url,
prompt: currentJob.parameters.prompt,
negativePrompt: currentJob.parameters.negative_prompt,
timestamp: Date.now(),
settings: currentJob.parameters
}));
// Update job with success result
get().updateJobStatus(currentJob.id, 'completed', { images }, undefined, jobDuration);
} else {
throw new Error('No images were generated');
}
```
- If `zod` is not already installed, run `npm install zod` and ensure the import is at the top of your file.
- If you do not want to use `zod`, replace the schema validation block with manual checks for the presence and type of `comfy_response.generated_images`.
- Move the `import { z } from "zod";` statement to the top of the file if you prefer to keep imports together.
</issue_to_address>
### Comment 4
<location> `dream_layer_frontend/src/features/MatrixRunner/MatrixProgressGrid.tsx:30` </location>
<code_context>
+ const getGridDimensions = () => {
</code_context>
<issue_to_address>
Grid layout logic may not generalize for all parameter combinations.
Currently, the grid only handles specific two-parameter combinations. If more than two parameters have multiple values, the visualization may become unclear. Consider adding a warning or supporting more flexible layouts for higher-dimensional cases.
Suggested implementation:
```typescript
const getGridDimensions = () => {
const paramCounts = {
seeds: parameters.seeds?.values.length || 1,
samplers: parameters.samplers?.values.length || 1,
steps: parameters.steps?.values.length || 1,
cfg_scale: parameters.cfg_scale?.values.length || 1,
};
// Count how many parameters have more than one value
const multiValueParams = Object.entries(paramCounts).filter(([_, count]) => count > 1);
// Warn if more than two parameters have multiple values
if (multiValueParams.length > 2) {
return {
warning: `Warning: More than two parameters (${multiValueParams.map(([k]) => k).join(', ')}) have multiple values. The grid layout may not visualize all combinations clearly.`,
rows: 1,
cols: 1,
xAxis: null,
yAxis: null,
};
}
// Determine grid layout based on parameter combinations
if (paramCounts.seeds > 1 && paramCounts.samplers > 1) {
return { rows: paramCounts.seeds, cols: paramCounts.samplers, xAxis: 'samplers', yAxis: 'seeds' };
} else if (paramCounts.steps > 1 && paramCounts.cfg_scale > 1) {
return { rows: paramCounts.steps, cols: paramCounts.cfg_scale, xAxis: 'cfg_scale', yAxis: 'steps' };
```
You will need to update the component that calls `getGridDimensions()` to check for and display the `warning` property if present. For example, after calling `const grid = getGridDimensions();`, you could render an alert or message if `grid.warning` exists.
</issue_to_address>
### Comment 5
<location> `README.md:164` </location>
<code_context>
-
+| 🔍 Feature | 🚀 How it’s better |
+| ------------------------------- | ----------------------------------------------------------------------------------------------------------- |
+| **Familiar Layout** | If you’ve used A1111 or Forge, you’ll feel at home in sec. Zero learning curve |
+| **Modern UX** | Responsive design with light & dark themes and a clutter-free interface that lets you work faster |
+| **ComfyUI Engine Inside** | All generation runs on a proven, modular, stable ComfyUI backend. Ready for custom nodes and advanced hacks |
</code_context>
<issue_to_address>
Possible typo: 'in sec.' should likely be 'in seconds'.
Consider updating 'in sec.' to 'in seconds' for clarity.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
| **Familiar Layout** | If you’ve used A1111 or Forge, you’ll feel at home in sec. Zero learning curve |
=======
| **Familiar Layout** | If you’ve used A1111 or Forge, you’ll feel at home in seconds. Zero learning curve |
>>>>>>> REPLACE
</suggested_fix>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
// IndexedDB storage adapter for Zustand | ||
const createIndexedDBStorage = () => { | ||
const dbName = 'dreamlayer-matrix-runner'; | ||
const storeName = 'matrix-runner-state'; | ||
const version = 1; | ||
|
||
return { | ||
getItem: async (name: string): Promise<string | null> => { | ||
return new Promise((resolve) => { | ||
const request = indexedDB.open(dbName, version); |
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.
suggestion: Consider fallback or error handling for environments without IndexedDB support.
IndexedDB may not be available in all environments. Add a fallback (e.g., localStorage or memory) or provide clear error messaging if persistence fails.
Suggested implementation:
/**
* IndexedDB storage adapter for Zustand with fallback to localStorage or in-memory storage.
*/
const createIndexedDBStorage = () => {
const dbName = 'dreamlayer-matrix-runner';
const storeName = 'matrix-runner-state';
const version = 1;
// In-memory fallback
const memoryStore: Record<string, string> = {};
// Check for IndexedDB support
const hasIndexedDB = typeof window !== 'undefined' && 'indexedDB' in window;
const hasLocalStorage = typeof window !== 'undefined' && 'localStorage' in window;
// IndexedDB methods
const indexedDBMethods = {
getItem: async (name: string): Promise<string | null> => {
return new Promise((resolve) => {
const request = indexedDB.open(dbName, version);
// localStorage methods
const localStorageMethods = {
getItem: async (name: string): Promise<string | null> => {
try {
return localStorage.getItem(name);
} catch (e) {
console.warn('localStorage getItem failed', e);
return null;
}
},
setItem: async (name: string, value: string): Promise<void> => {
try {
localStorage.setItem(name, value);
} catch (e) {
console.warn('localStorage setItem failed', e);
}
},
removeItem: async (name: string): Promise<void> => {
try {
localStorage.removeItem(name);
} catch (e) {
console.warn('localStorage removeItem failed', e);
}
}
};
// In-memory methods
const memoryMethods = {
getItem: async (name: string): Promise<string | null> => {
return memoryStore[name] ?? null;
},
setItem: async (name: string, value: string): Promise<void> => {
memoryStore[name] = value;
},
removeItem: async (name: string): Promise<void> => {
delete memoryStore[name];
}
};
// Select storage backend
let storageBackend;
if (hasIndexedDB) {
storageBackend = indexedDBMethods;
} else if (hasLocalStorage) {
console.warn('IndexedDB not available, falling back to localStorage for persistence.');
storageBackend = localStorageMethods;
} else {
console.warn('Neither IndexedDB nor localStorage available, falling back to in-memory storage. Persistence will not survive reloads.');
storageBackend = memoryMethods;
}
return storageBackend;
const response = await fetch('http://localhost:5001/api/txt2img', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify(currentJob.parameters), | ||
}); |
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.
suggestion: Hardcoded API endpoint may limit deployment flexibility.
Consider making the API endpoint configurable through environment variables or a global config to allow independent deployment of frontend and backend.
const response = await fetch('http://localhost:5001/api/txt2img', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify(currentJob.parameters), | |
}); | |
// Use environment variable for API base URL, fallback to localhost if not set | |
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5001'; | |
const response = await fetch(`${API_BASE_URL}/api/txt2img`, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify(currentJob.parameters), | |
}); |
...state.parameters, | ||
[param]: range | ||
} | ||
})); |
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.
suggestion (bug_risk): Assumes a specific backend response structure.
Consider adding error handling or schema validation to handle potential changes in the backend response format.
Suggested implementation:
const data = await response.json();
const endTime = Date.now();
const jobDuration = (endTime - startTime) / 1000; // Convert to seconds
// Schema validation for backend response
// If zod is not available, replace with manual checks as needed
import { z } from "zod";
const ComfyResponseSchema = z.object({
comfy_response: z.object({
generated_images: z.array(
z.object({
url: z.string().url()
})
)
})
});
const parseResult = ComfyResponseSchema.safeParse(data);
if (!parseResult.success) {
console.error('Backend response schema validation failed:', parseResult.error);
throw new Error('Unexpected backend response format');
}
const generatedImages = parseResult.data.comfy_response.generated_images;
if (generatedImages && generatedImages.length > 0) {
const images = generatedImages.map((img: any) => ({
id: `${Date.now()}-${Math.random()}`,
url: img.url,
prompt: currentJob.parameters.prompt,
negativePrompt: currentJob.parameters.negative_prompt,
timestamp: Date.now(),
settings: currentJob.parameters
}));
// Update job with success result
get().updateJobStatus(currentJob.id, 'completed', { images }, undefined, jobDuration);
} else {
throw new Error('No images were generated');
}
- If
zod
is not already installed, runnpm install zod
and ensure the import is at the top of your file. - If you do not want to use
zod
, replace the schema validation block with manual checks for the presence and type ofcomfy_response.generated_images
. - Move the
import { z } from "zod";
statement to the top of the file if you prefer to keep imports together.
</CardContent> | ||
</Card> | ||
); | ||
} |
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.
suggestion: Grid layout logic may not generalize for all parameter combinations.
Currently, the grid only handles specific two-parameter combinations. If more than two parameters have multiple values, the visualization may become unclear. Consider adding a warning or supporting more flexible layouts for higher-dimensional cases.
Suggested implementation:
const getGridDimensions = () => {
const paramCounts = {
seeds: parameters.seeds?.values.length || 1,
samplers: parameters.samplers?.values.length || 1,
steps: parameters.steps?.values.length || 1,
cfg_scale: parameters.cfg_scale?.values.length || 1,
};
// Count how many parameters have more than one value
const multiValueParams = Object.entries(paramCounts).filter(([_, count]) => count > 1);
// Warn if more than two parameters have multiple values
if (multiValueParams.length > 2) {
return {
warning: `Warning: More than two parameters (${multiValueParams.map(([k]) => k).join(', ')}) have multiple values. The grid layout may not visualize all combinations clearly.`,
rows: 1,
cols: 1,
xAxis: null,
yAxis: null,
};
}
// Determine grid layout based on parameter combinations
if (paramCounts.seeds > 1 && paramCounts.samplers > 1) {
return { rows: paramCounts.seeds, cols: paramCounts.samplers, xAxis: 'samplers', yAxis: 'seeds' };
} else if (paramCounts.steps > 1 && paramCounts.cfg_scale > 1) {
return { rows: paramCounts.steps, cols: paramCounts.cfg_scale, xAxis: 'cfg_scale', yAxis: 'steps' };
You will need to update the component that calls getGridDimensions()
to check for and display the warning
property if present. For example, after calling const grid = getGridDimensions();
, you could render an alert or message if grid.warning
exists.
|
||
| 🔍 Feature | 🚀 How it’s better | | ||
| ------------------------------- | ----------------------------------------------------------------------------------------------------------- | | ||
| **Familiar Layout** | If you’ve used A1111 or Forge, you’ll feel at home in sec. Zero learning curve | |
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.
issue (typo): Possible typo: 'in sec.' should likely be 'in seconds'.
Consider updating 'in sec.' to 'in seconds' for clarity.
| **Familiar Layout** | If you’ve used A1111 or Forge, you’ll feel at home in sec. Zero learning curve | | |
| **Familiar Layout** | If you’ve used A1111 or Forge, you’ll feel at home in seconds. Zero learning curve | |
raise Exception( | ||
f"Stability Upscale Fast failed: {response_api.finish_reason}.") |
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.
issue (code-quality): Raise a specific error instead of the general Exception
or BaseException
(raise-specific-error
)
Explanation
If a piece of code raises a specific exception type rather than the generic [`BaseException`](https://docs.python.org/3/library/exceptions.html#BaseException) or [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception), the calling code can:- get more information about what type of error it is
- define specific exception handling for it
This way, callers of the code can handle the error appropriately.
How can you solve this?
- Use one of the built-in exceptions of the standard library.
- Define your own error class that subclasses
Exception
.
So instead of having code raising Exception
or BaseException
like
if incorrect_input(value):
raise Exception("The input is incorrect")
you can have code raising a specific error like
if incorrect_input(value):
raise ValueError("The input is incorrect")
or
class IncorrectInputError(Exception):
pass
if incorrect_input(value):
raise IncorrectInputError("The input is incorrect")
"Content-Type": "application/json" | ||
} | ||
|
||
try: |
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.
issue (code-quality): We've found these issues:
- Extract code out into method (
extract-method
) - Explicitly raise from a previous error [×2] (
raise-from-previous-error
)
api_key = os.getenv(env_key) | ||
if api_key: | ||
api_keys[env_key] = api_key | ||
# Safely truncate for display without assuming length | ||
display_key = api_key[:8] + "..." + api_key[-4:] if len(api_key) > 12 else api_key | ||
display_key = api_key[:8] + "..." + \ | ||
api_key[-4:] if len(api_key) > 12 else api_key | ||
print(f"[DEBUG] Found {env_key}: {display_key}") | ||
else: | ||
print(f"[DEBUG] No {env_key} found in environment") |
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.
issue (code-quality): We've found these issues:
- Use named expression to simplify assignment and conditional (
use-named-expression
) - Use f-string instead of string concatenation [×2] (
use-fstring-for-concatenation
)
# Try to find name parameter in the data URL | ||
name_match = re.search(r';name=(.*?);', data_url) | ||
if name_match: | ||
return name_match.group(1) |
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.
suggestion (code-quality): We've found these issues:
- Use named expression to simplify assignment and conditional (
use-named-expression
) - Replace m.group(x) with m[x] for re.Match objects (
use-getitem-for-re-match-groups
)
# Try to find name parameter in the data URL | |
name_match = re.search(r';name=(.*?);', data_url) | |
if name_match: | |
return name_match.group(1) | |
if name_match := re.search(r';name=(.*?);', data_url): | |
return name_match[1] |
from shared_utils import SAMPLER_NAME_MAP | ||
|
||
|
||
def transform_to_txt2img_workflow(data): |
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.
issue (code-quality): We've found these issues:
- Replace f-string with no interpolated values with string [×5] (
remove-redundant-fstring
) - Low code quality found in transform_to_txt2img_workflow - 19% (
low-code-quality
)
Explanation
The quality score for this function is below the quality threshold of 25%.
This score is a combination of the method length, cognitive complexity and working memory.
How can you solve this?
It might be worth refactoring this function to make it shorter and more readable.
- Reduce the function length by extracting pieces of functionality out into
their own functions. This is the most important thing you can do - ideally a
function should be less than 10 lines. - Reduce nesting, perhaps by introducing guard clauses to return early.
- Ensure that variables are tightly scoped, so that code using related concepts
sits together within the function rather than being scattered.
Description
This PR implements Task #2: Matrix Runner for the DreamLayer coding challenge. The Matrix Runner allows users to create parameter sweeps across multiple generation settings including seeds, steps, samplers, and CFG values, with pause/resume functionality and deterministic job execution.
Changes Made
Evidence Required
UI Screenshot
Generated Image
Note: Due to local development environment limitations, I wasn't able to capture AI-generated images. However, the Matrix Runner functionality is fully implemented and tested as shown in the logs and UI screenshot above.
Logs
PASS src/features/MatrixRunner/tests/MatrixRunner.integration.test.tsx
Matrix Runner Integration Tests
Task #2 Requirements
✓ 3×2 sweep completes jobs exactly once (593 ms)
✓ Pause and resume operate without creating duplicates (146 ms)
✓ State survives page refresh (16 ms)
✓ Deterministic job list generation (1 ms)
✓ Parameter parsing handles various formats (18 ms)
Advanced Features
✓ Auto batching groups similar jobs
Test Suites: 1 passed, 1 total
Tests: 6 passed, 6 total
Tests (Optional)
✓ Form accepts lists or ranges for seeds, steps, samplers and similar fields
✓ Expands to a deterministic job list and shows the total count
✓ Pause and resume operate without creating duplicates and survive page refresh
✓ Integration test runs a 3 by 2 sweep that completes each job exactly once
Checklist
Summary by Sourcery
Implement the Matrix Runner feature for parameter sweeping and integrate direct Stability AI API support, enhance backend workflows and utilities for STABILITY_API_KEY, update documentation, and add integration tests with Jest for robust end-to-end coverage.
New Features:
Enhancements:
CI:
Documentation:
Tests: