proxy, ui-svelte: add /sdapi/v1 endpoint support#587
Conversation
Add proxy routes for stable-diffusion.cpp's /sdapi/v1/txt2img, /sdapi/v1/img2img, and /sdapi/v1/loras endpoints. POST endpoints use proxyInferenceHandler (model in JSON body), GET /loras uses proxyGETModelHandler (model in query param). Update the image playground with a dual-mode UI supporting both OpenAI and SDAPI backends. In SDAPI mode, loras are fetched first to prime the server-side cache, and all txt2img parameters are exposed (negative prompt, steps, cfg_scale, seed, batch_size, clip_skip, sampler, scheduler, lora selection with multipliers). - Add 3 sdapi route registrations in proxymanager.go - Add sdApi.ts client with generateSdImage and fetchSdLoras - Add SDAPI types (SdApiTxt2ImgRequest, SdApiResponse, etc.) - Add /sdapi to vite dev proxy config - Add backend tests for sdapi routing - Support batch image display in gallery grid https://claude.ai/code/session_0186MGX6NXdHVBTv2KH45fqn
Replace automatic lora fetching on model change with a manual "Load LoRAs" button. Once loaded, loras are added via a select dropdown and displayed as a list with multiplier inputs and remove buttons. - Remove auto-fetch $effect for loras - Remove lora cache priming from generate flow - Add "Load LoRAs" / "Reload LoRAs" button - Add select dropdown to pick from available loras - Show selected loras as list with multiplier and remove controls https://claude.ai/code/session_0186MGX6NXdHVBTv2KH45fqn
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds Stable Diffusion API (SDAPI) HTTP routes and tests on the backend; introduces SDAPI client helpers, types, Vite proxy, and Svelte UI support (mode switch, multi-image results, and LoRA management); adds SDAPI stub endpoints in the simple-responder dev server. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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.
Actionable comments posted: 1
🧹 Nitpick comments (2)
ui-svelte/src/components/playground/ImageInterface.svelte (1)
48-63: Consider clearing LoRA state when the model changes.When the user switches to a different model, the
availableLoras,selectedLoras, andlorasLoadedstate remain from the previous model. This could lead to using LoRAs that don't exist for the new model.Add an effect to reset LoRA state on model change
+ // Reset LoRA state when model changes + $effect(() => { + const _ = $selectedModelStore; // track dependency + availableLoras = []; + selectedLoras = []; + lorasLoaded = false; + loraError = null; + }); + async function loadLoras() {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui-svelte/src/components/playground/ImageInterface.svelte` around lines 48 - 63, When the selected model changes the component must clear LoRA-related state to avoid carrying over invalid selections; update the component so that on model change (watching $selectedModelStore) you reset availableLoras = [], selectedLoras = [], and lorasLoaded = false before (or immediately when) calling loadLoras; locate the loadLoras function and the reactive/store usage of selectedModelStore and add a short effect or reactive statement tied to selectedModelStore that clears those variables and then triggers loadLoras to fetch the correct list for the new model.ui-svelte/src/lib/sdApi.ts (1)
16-22: Consider handling JSON parse errors.If the server returns a non-JSON response (e.g., during maintenance),
response.json()will throw an unhandled exception. The same applies tofetchSdLoras.Proposed defensive handling
if (!response.ok) { const errorText = await response.text(); throw new Error(`SDAPI error: ${response.status} - ${errorText}`); } - return response.json(); + try { + return await response.json(); + } catch { + throw new Error("SDAPI error: Invalid JSON response"); + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui-svelte/src/lib/sdApi.ts` around lines 16 - 22, Wrap the response.json() call in a try/catch in this file (the same pattern for fetchSdLoras) so JSON parse failures are handled: after checking response.ok capture response.text() inside a try block, attempt JSON.parse via await response.json(), and if that throws, catch the error, read the raw text (await response.text() if not already), and throw a new Error that includes response.status and the raw response body plus the parse error message; apply this change to the function containing the shown snippet and to fetchSdLoras so both return valid parsed JSON on success or throw a clear, informative error on non-JSON or malformed responses.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@ui-svelte/src/components/playground/ImageInterface.svelte`:
- Around line 100-116: The size parsing for the SD API branch is fragile: when
isSdapi is true the code splits $selectedSizeStore into w and h without
validation, which can yield NaN and break the API request; update the logic
around the isSdapi block to validate $selectedSizeStore (e.g., ensure it matches
/^\d+x\d+$/ or that split results are two numeric values), parse ints safely for
w and h, and handle invalid formats by using a safe default size or
early-return/error (affecting the width/height fields used when building
request) so request.width and request.height are never NaN.
---
Nitpick comments:
In `@ui-svelte/src/components/playground/ImageInterface.svelte`:
- Around line 48-63: When the selected model changes the component must clear
LoRA-related state to avoid carrying over invalid selections; update the
component so that on model change (watching $selectedModelStore) you reset
availableLoras = [], selectedLoras = [], and lorasLoaded = false before (or
immediately when) calling loadLoras; locate the loadLoras function and the
reactive/store usage of selectedModelStore and add a short effect or reactive
statement tied to selectedModelStore that clears those variables and then
triggers loadLoras to fetch the correct list for the new model.
In `@ui-svelte/src/lib/sdApi.ts`:
- Around line 16-22: Wrap the response.json() call in a try/catch in this file
(the same pattern for fetchSdLoras) so JSON parse failures are handled: after
checking response.ok capture response.text() inside a try block, attempt
JSON.parse via await response.json(), and if that throws, catch the error, read
the raw text (await response.text() if not already), and throw a new Error that
includes response.status and the raw response body plus the parse error message;
apply this change to the function containing the shown snippet and to
fetchSdLoras so both return valid parsed JSON on success or throw a clear,
informative error on non-JSON or malformed responses.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6f2a93b4-c822-438e-a60f-708320de4592
📒 Files selected for processing (6)
proxy/proxymanager.goproxy/proxymanager_test.goui-svelte/src/components/playground/ImageInterface.svelteui-svelte/src/lib/sdApi.tsui-svelte/src/lib/types.tsui-svelte/vite.config.ts
| if (isSdapi) { | ||
| const [w, h] = $selectedSizeStore.split("x").map(Number); | ||
| const request = { | ||
| model: $selectedModelStore, | ||
| prompt: trimmedPrompt, | ||
| negative_prompt: $sdNegativePromptStore || undefined, | ||
| width: w, | ||
| height: h, | ||
| steps: $sdStepsStore, | ||
| cfg_scale: $sdCfgScaleStore, | ||
| seed: $sdSeedStore, | ||
| batch_size: $sdBatchSizeStore, | ||
| clip_skip: $sdClipSkipStore, | ||
| sampler_name: $sdSamplerStore || undefined, | ||
| scheduler: $sdSchedulerStore || undefined, | ||
| lora: selectedLoras.length > 0 ? selectedLoras : undefined, | ||
| }; |
There was a problem hiding this comment.
Add validation for size parsing.
The size string is split without validation. If $selectedSizeStore has an unexpected format, w and h could be NaN, causing API errors.
Proposed fix with validation
if (isSdapi) {
const [w, h] = $selectedSizeStore.split("x").map(Number);
+ if (isNaN(w) || isNaN(h) || w <= 0 || h <= 0) {
+ error = "Invalid image size format";
+ return;
+ }
const request = {📝 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.
| if (isSdapi) { | |
| const [w, h] = $selectedSizeStore.split("x").map(Number); | |
| const request = { | |
| model: $selectedModelStore, | |
| prompt: trimmedPrompt, | |
| negative_prompt: $sdNegativePromptStore || undefined, | |
| width: w, | |
| height: h, | |
| steps: $sdStepsStore, | |
| cfg_scale: $sdCfgScaleStore, | |
| seed: $sdSeedStore, | |
| batch_size: $sdBatchSizeStore, | |
| clip_skip: $sdClipSkipStore, | |
| sampler_name: $sdSamplerStore || undefined, | |
| scheduler: $sdSchedulerStore || undefined, | |
| lora: selectedLoras.length > 0 ? selectedLoras : undefined, | |
| }; | |
| if (isSdapi) { | |
| const [w, h] = $selectedSizeStore.split("x").map(Number); | |
| if (isNaN(w) || isNaN(h) || w <= 0 || h <= 0) { | |
| error = "Invalid image size format"; | |
| return; | |
| } | |
| const request = { | |
| model: $selectedModelStore, | |
| prompt: trimmedPrompt, | |
| negative_prompt: $sdNegativePromptStore || undefined, | |
| width: w, | |
| height: h, | |
| steps: $sdStepsStore, | |
| cfg_scale: $sdCfgScaleStore, | |
| seed: $sdSeedStore, | |
| batch_size: $sdBatchSizeStore, | |
| clip_skip: $sdClipSkipStore, | |
| sampler_name: $sdSamplerStore || undefined, | |
| scheduler: $sdSchedulerStore || undefined, | |
| lora: selectedLoras.length > 0 ? selectedLoras : undefined, | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ui-svelte/src/components/playground/ImageInterface.svelte` around lines 100 -
116, The size parsing for the SD API branch is fragile: when isSdapi is true the
code splits $selectedSizeStore into w and h without validation, which can yield
NaN and break the API request; update the logic around the isSdapi block to
validate $selectedSizeStore (e.g., ensure it matches /^\d+x\d+$/ or that split
results are two numeric values), parse ints safely for w and h, and handle
invalid formats by using a safe default size or early-return/error (affecting
the width/height fields used when building request) so request.width and
request.height are never NaN.
Remove clip skip option and replace sampler/scheduler text inputs with dropdown selects populated with known values from sd.cpp. - remove clip_skip from SdApiTxt2ImgRequest type and UI - sampler dropdown: euler_a, euler, heun, dpm2, dpmpp2s_a, dpmpp2m, dpmpp2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd - scheduler dropdown: discrete, karras, exponential, ays, gits https://claude.ai/code/session_0186MGX6NXdHVBTv2KH45fqn
Add /sdapi/v1/txt2img, /sdapi/v1/img2img, and /sdapi/v1/loras handlers to simple-responder so SD API routing tests can pass. https://claude.ai/code/session_0186MGX6NXdHVBTv2KH45fqn
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
cmd/simple-responder/simple-responder.go (1)
219-231: Deduplicate the two SDAPI POST echo handlers to avoid behavior drift.Line 219-231 duplicates identical logic for
txt2imgandimg2img; extracting one shared handler keeps both endpoints consistent and easier to maintain.Proposed refactor
+echoRequestBody := func(c *gin.Context) { + bodyBytes, err := io.ReadAll(c.Request.Body) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("failed to read request body: %s", err)}) + return + } + c.JSON(http.StatusOK, gin.H{ + "request_body": string(bodyBytes), + }) +} + // sd.cpp endpoints -r.POST("/sdapi/v1/txt2img", func(c *gin.Context) { - bodyBytes, _ := io.ReadAll(c.Request.Body) - c.JSON(http.StatusOK, gin.H{ - "request_body": string(bodyBytes), - }) -}) +r.POST("/sdapi/v1/txt2img", echoRequestBody) -r.POST("/sdapi/v1/img2img", func(c *gin.Context) { - bodyBytes, _ := io.ReadAll(c.Request.Body) - c.JSON(http.StatusOK, gin.H{ - "request_body": string(bodyBytes), - }) -}) +r.POST("/sdapi/v1/img2img", echoRequestBody)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/simple-responder/simple-responder.go` around lines 219 - 231, The two POST route handlers for "/sdapi/v1/txt2img" and "/sdapi/v1/img2img" duplicate the same request-body echo logic; extract a single handler function (e.g., sdapiEchoHandler) that reads c.Request.Body (using io.ReadAll and handling the error) and returns c.JSON(http.StatusOK, gin.H{"request_body": string(bodyBytes)}), then register that same sdapiEchoHandler for both r.POST("/sdapi/v1/txt2img", ...) and r.POST("/sdapi/v1/img2img", ...) to keep behavior consistent and maintainable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cmd/simple-responder/simple-responder.go`:
- Around line 219-223: The POST handler for "/sdapi/v1/txt2img" currently
ignores errors from io.ReadAll and returns 200 with possibly empty request_body;
update the handler(s) that call io.ReadAll (the "/sdapi/v1/txt2img" route and
the similar handler around lines 226-230) to check the error returned, and if
non-nil respond with an appropriate non-200 status and JSON error (e.g., use
c.AbortWithStatusJSON or c.JSON with
http.StatusBadRequest/http.StatusInternalServerError) including an error
message, otherwise continue to return the request_body; ensure you reference the
bodyBytes and err variables in the handler to guard against nil/partial reads.
---
Nitpick comments:
In `@cmd/simple-responder/simple-responder.go`:
- Around line 219-231: The two POST route handlers for "/sdapi/v1/txt2img" and
"/sdapi/v1/img2img" duplicate the same request-body echo logic; extract a single
handler function (e.g., sdapiEchoHandler) that reads c.Request.Body (using
io.ReadAll and handling the error) and returns c.JSON(http.StatusOK,
gin.H{"request_body": string(bodyBytes)}), then register that same
sdapiEchoHandler for both r.POST("/sdapi/v1/txt2img", ...) and
r.POST("/sdapi/v1/img2img", ...) to keep behavior consistent and maintainable.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 1a0e6395-2ee5-4670-b3f3-200f0707e025
📒 Files selected for processing (2)
cmd/simple-responder/simple-responder.gogo.mod
✅ Files skipped from review due to trivial changes (1)
- go.mod
Add handlers for sdapi/v1 endpoints to support testing. - POST /sdapi/v1/txt2img - POST /sdapi/v1/img2img - GET /sdapi/v1/loras Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cmd/simple-responder/simple-responder.go`:
- Around line 218-236: Remove the duplicate early route registrations for the SD
endpoints by deleting the first r.POST handlers for "/sdapi/v1/txt2img" and
"/sdapi/v1/img2img" and the r.GET handler for "/sdapi/v1/loras" (the handlers
that simply read c.Request.Body without error handling) so only the later
implementations remain (those around the other registration block with proper
io.ReadAll error checks); after removing the duplicate block, run gofmt -w on
simple-responder.go to fix the formatting issues.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5c7e56df-d424-41e8-b73c-78ab227e20b8
📒 Files selected for processing (1)
cmd/simple-responder/simple-responder.go
Add proxy routes for stable-diffusion.cpp's /sdapi/v1/txt2img, /sdapi/v1/img2img, and /sdapi/v1/loras endpoints. POST endpoints use proxyInferenceHandler (model in JSON body), GET /loras uses proxyGETModelHandler (model in query param). Update the image playground with a dual-mode UI supporting both OpenAI and SDAPI backends. In SDAPI mode, loras are fetched first to prime the server-side cache, and all txt2img parameters are exposed (negative prompt, steps, cfg_scale, seed, batch_size, clip_skip, sampler, scheduler, lora selection with multipliers). - Add 3 sdapi route registrations in proxymanager.go - Add sdApi.ts client with generateSdImage and fetchSdLoras - Add SDAPI types (SdApiTxt2ImgRequest, SdApiResponse, etc.) - Add /sdapi to vite dev proxy config - Add backend tests for sdapi routing - Support batch image display in gallery grid https://claude.ai/code/session_0186MGX6NXdHVBTv2KH45fqn --------- Co-authored-by: Claude <[email protected]>
Add proxy routes for stable-diffusion.cpp's /sdapi/v1/txt2img,
/sdapi/v1/img2img, and /sdapi/v1/loras endpoints. POST endpoints
use proxyInferenceHandler (model in JSON body), GET /loras uses
proxyGETModelHandler (model in query param).
Update the image playground with a dual-mode UI supporting both
OpenAI and SDAPI backends. In SDAPI mode, loras are fetched first
to prime the server-side cache, and all txt2img parameters are
exposed (negative prompt, steps, cfg_scale, seed, batch_size,
clip_skip, sampler, scheduler, lora selection with multipliers).
https://claude.ai/code/session_0186MGX6NXdHVBTv2KH45fqn