@@ -854,7 +854,8 @@ void worker_thread() {
854
854
task_queue.pop ();
855
855
lock.unlock ();
856
856
task ();
857
- is_busy = false ;
857
+ is_busy = false ;
858
+ running_task_id = " " ;
858
859
}
859
860
}
860
861
}
@@ -926,14 +927,16 @@ void start_server(SDParams params) {
926
927
using json = nlohmann::json;
927
928
std::string task_id = std::to_string (std::chrono::system_clock::now ().time_since_epoch ().count ());
928
929
929
- json pending_task_json = json::object ();
930
- pending_task_json[" status" ] = " Pending" ;
931
- pending_task_json[" data" ] = json::array ();
932
- pending_task_json[" step" ] = -1 ;
933
- pending_task_json[" eta" ] = " ?" ;
930
+ {
931
+ json pending_task_json = json::object ();
932
+ pending_task_json[" status" ] = " Pending" ;
933
+ pending_task_json[" data" ] = json::array ();
934
+ pending_task_json[" step" ] = -1 ;
935
+ pending_task_json[" eta" ] = " ?" ;
934
936
935
- std::lock_guard<std::mutex> results_lock (results_mutex);
936
- task_results[task_id] = pending_task_json;
937
+ std::lock_guard<std::mutex> results_lock (results_mutex);
938
+ task_results[task_id] = pending_task_json;
939
+ }
937
940
938
941
auto task = [&req, &sd_ctx, ¶ms, &n_prompts, task_id]() {
939
942
running_task_id = task_id;
@@ -1044,12 +1047,7 @@ void start_server(SDParams params) {
1044
1047
end_task_json[" eta" ] = " ?" ;
1045
1048
std::lock_guard<std::mutex> results_lock (results_mutex);
1046
1049
task_results[task_id] = end_task_json;
1047
- return ;
1048
1050
}
1049
-
1050
- std::lock_guard<std::mutex> results_lock (results_mutex);
1051
- task_results[task_id][" status" ] = " Failed" ;
1052
- return ;
1053
1051
};
1054
1052
// Add the task to the queue
1055
1053
add_task (task_id, task);
@@ -1059,20 +1057,321 @@ void start_server(SDParams params) {
1059
1057
res.set_content (response.dump (), " application/json" );
1060
1058
});
1061
1059
1062
- svr->Post (" /result" , [](const httplib::Request& req, httplib::Response& res) {
1060
+ svr->Get (" /params" , [¶ms](const httplib::Request& req, httplib::Response& res) {
1061
+ using json = nlohmann::json;
1062
+ json response;
1063
+ json params_json = json::object ();
1064
+ params_json[" prompt" ] = params.lastRequest .prompt ;
1065
+ params_json[" negative_prompt" ] = params.lastRequest .negative_prompt ;
1066
+ params_json[" clip_skip" ] = params.lastRequest .clip_skip ;
1067
+ params_json[" cfg_scale" ] = params.lastRequest .cfg_scale ;
1068
+ params_json[" guidance" ] = params.lastRequest .guidance ;
1069
+ params_json[" width" ] = params.lastRequest .width ;
1070
+ params_json[" height" ] = params.lastRequest .height ;
1071
+ params_json[" sample_method" ] = sample_method_str[params.lastRequest .sample_method ];
1072
+ params_json[" sample_steps" ] = params.lastRequest .sample_steps ;
1073
+ params_json[" seed" ] = params.lastRequest .seed ;
1074
+ params_json[" batch_count" ] = params.lastRequest .batch_count ;
1075
+ params_json[" normalize_input" ] = params.lastRequest .normalize_input ;
1076
+ // params_json["input_id_images_path"] = params.input_id_images_path;
1077
+ response[" generation_params" ] = params_json;
1078
+
1079
+ json context_params = json::object ();
1080
+ // Do not expose paths
1081
+ // context_params["model_path"] = params.ctxParams.model_path;
1082
+ // context_params["clip_l_path"] = params.ctxParams.clip_l_path;
1083
+ // context_params["clip_g_path"] = params.ctxParams.clip_g_path;
1084
+ // context_params["t5xxl_path"] = params.ctxParams.t5xxl_path;
1085
+ // context_params["diffusion_model_path"] = params.ctxParams.diffusion_model_path;
1086
+ // context_params["vae_path"] = params.ctxParams.vae_path;
1087
+ // context_params["controlnet_path"] = params.ctxParams.controlnet_path;
1088
+ context_params[" lora_model_dir" ] = params.ctxParams .lora_model_dir ;
1089
+ // context_params["embeddings_path"] = params.ctxParams.embeddings_path;
1090
+ // context_params["stacked_id_embeddings_path"] = params.ctxParams.stacked_id_embeddings_path;
1091
+ context_params[" vae_decode_only" ] = params.ctxParams .vae_decode_only ;
1092
+ context_params[" vae_tiling" ] = params.ctxParams .vae_tiling ;
1093
+ context_params[" n_threads" ] = params.ctxParams .n_threads ;
1094
+ context_params[" wtype" ] = params.ctxParams .wtype ;
1095
+ context_params[" rng_type" ] = params.ctxParams .rng_type ;
1096
+ context_params[" schedule" ] = params.ctxParams .schedule ;
1097
+ context_params[" clip_on_cpu" ] = params.ctxParams .clip_on_cpu ;
1098
+ context_params[" control_net_cpu" ] = params.ctxParams .control_net_cpu ;
1099
+ context_params[" vae_on_cpu" ] = params.ctxParams .vae_on_cpu ;
1100
+ context_params[" diffusion_flash_attn" ] = params.ctxParams .diffusion_flash_attn ;
1101
+ response[" context_params" ] = context_params;
1102
+
1103
+ res.set_content (response.dump (), " application/json" );
1104
+ });
1105
+
1106
+
1107
+ svr->Get (" /result" , [](const httplib::Request& req, httplib::Response& res) {
1063
1108
using json = nlohmann::json;
1064
1109
// Parse task ID from query parameters
1065
1110
try {
1066
- std::string task_id = json::parse ( req.body )[ " task_id" ] ;
1111
+ std::string task_id = req.get_param_value ( " task_id" ) ;
1067
1112
std::lock_guard<std::mutex> lock (results_mutex);
1068
1113
if (task_results.find (task_id) != task_results.end ()) {
1069
1114
json result = task_results[task_id];
1070
1115
res.set_content (result.dump (), " application/json" );
1116
+ // Erase data after sending
1117
+ result[" data" ] = json::array ();
1118
+ task_results[task_id] = result;
1071
1119
} else {
1072
1120
res.set_content (" Cannot find task " + task_id + " in queue" , " text/plain" );
1073
1121
}
1074
1122
} catch (...) {
1075
- sd_log (sd_log_level_t ::SD_LOG_WARN, " Invalid request body: %s\n " , req.body .c_str ());
1123
+ sd_log (sd_log_level_t ::SD_LOG_WARN, " Error when fetching result" );
1124
+ }
1125
+ });
1126
+
1127
+ svr->Get (" /sample_methods" , [](const httplib::Request& req, httplib::Response& res) {
1128
+ using json = nlohmann::json;
1129
+ json response;
1130
+ for (int m = 0 ; m < N_SAMPLE_METHODS; m++) {
1131
+ response.push_back (sample_method_str[m]);
1132
+ }
1133
+ res.set_content (response.dump (), " application/json" );
1134
+ });
1135
+
1136
+ svr->Get (" /schedules" , [](const httplib::Request& req, httplib::Response& res) {
1137
+ using json = nlohmann::json;
1138
+ json response;
1139
+ for (int s = 0 ; s < N_SCHEDULES; s++) {
1140
+ response.push_back (schedule_str[s]);
1141
+ }
1142
+ res.set_content (response.dump (), " application/json" );
1143
+ });
1144
+
1145
+
1146
+ svr->Get (" /index.html" , [](const httplib::Request& req, httplib::Response& res) {
1147
+ try {
1148
+ std::string html_content = R"xxx(
1149
+ <!DOCTYPE html>
1150
+ <html>
1151
+ <head>
1152
+ <meta charset="UTF-8">
1153
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1154
+ <title>SDCPP Server</title>
1155
+ <style>
1156
+ body {
1157
+ font-family: Arial, sans-serif;
1158
+ display: flex;
1159
+ align-items: center;
1160
+ justify-content: center;
1161
+ height: 100vh;
1162
+ margin: 0;
1163
+ background-color: #f0f0f0;
1164
+ }
1165
+ .container {
1166
+ display: flex;
1167
+ width: 80%;
1168
+ background: white;
1169
+ padding: 20px;
1170
+ border-radius: 10px;
1171
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
1172
+ }
1173
+ .input-group {
1174
+ display: flex;
1175
+ align-items: center;
1176
+ margin-bottom: 10px;
1177
+ }
1178
+ .input-group label {
1179
+ width: 150px;
1180
+ text-align: right;
1181
+ margin-right: 10px;
1182
+ }
1183
+ .prompt-input, .param-input {
1184
+ width: 400px;
1185
+ }
1186
+ canvas {
1187
+ border: 1px solid #ccc;
1188
+ }
1189
+ .left-section {
1190
+ flex: 1;
1191
+ padding-right: 20px;
1192
+ }
1193
+ .right-section {
1194
+ flex: 1;
1195
+ display: flex;
1196
+ align-items: center;
1197
+ justify-content: center;
1198
+ }
1199
+ </style>
1200
+ </head>
1201
+ <body>
1202
+ <div class="container">
1203
+ <div class="left-section">
1204
+ <h1>SDCPP Server</h1>
1205
+ <div id="prompts">
1206
+ <div class="input-group">
1207
+ <label for="prompt">Prompt:</label>
1208
+ <input type="text" id="prompt" class="prompt-input">
1209
+ </div>
1210
+ <div class="input-group">
1211
+ <label for="neg_prompt">Negative Prompt:</label>
1212
+ <input type="text" id="neg_prompt" class="prompt-input">
1213
+ </div>
1214
+ </div>
1215
+ <div id="params">
1216
+ <div class="input-group">
1217
+ <label for="width">Width:</label>
1218
+ <input type="number" id="width" class="param-input">
1219
+ </div>
1220
+ <div class="input-group">
1221
+ <label for="height">Height:</label>
1222
+ <input type="number" id="height" class="param-input">
1223
+ </div>
1224
+ <div class="input-group">
1225
+ <label for="cfg_scale">CFG Scale:</label>
1226
+ <input type="number" id="cfg_scale" class="param-input">
1227
+ </div>
1228
+ <div class="input-group">
1229
+ <label for="guidance">Guidance (Flux):</label>
1230
+ <input type="number" id="guidance" class="param-input">
1231
+ </div>
1232
+ <div class="input-group">
1233
+ <label for="steps">Steps:</label>
1234
+ <input type="number" id="steps" class="param-input">
1235
+ </div>
1236
+ <div class="input-group">
1237
+ <label for="sample_method">Sample Method:</label>
1238
+ <select id="sample_method" class="param-input"></select>
1239
+ </div>
1240
+ <div class="input-group">
1241
+ <label for="seed">Seed:</label>
1242
+ <input type="number" id="seed" class="param-input">
1243
+ </div>
1244
+ <div class="input-group">
1245
+ <label for="batch_count">Batch Count:</label>
1246
+ <input type="number" id="batch_count" class="param-input">
1247
+ </div>
1248
+ </div>
1249
+ <button onclick="generateImage()">Generate</button>
1250
+ <a id="downloadLink" style="display: none;" download="generated_image.png">Download Image</a>
1251
+ </div>
1252
+ <div class="right-section">
1253
+ <canvas id="imageCanvas" width="500" height="500"></canvas>
1254
+ </div>
1255
+ </div>
1256
+ <script>
1257
+ // Fetch sample methods from the server and populate the dropdown list
1258
+ async function fetchSampleMethods() {
1259
+ const response = await fetch('/sample_methods');
1260
+ const data = await response.json();
1261
+
1262
+ const select = document.getElementById('sample_method');
1263
+ data.forEach(method => {
1264
+ const option = document.createElement('option');
1265
+ option.value = method;
1266
+ option.textContent = method;
1267
+ select.appendChild(option);
1268
+ });
1269
+ }
1270
+
1271
+ // Call the function to fetch and populate the sample methods list
1272
+ fetchSampleMethods();
1273
+
1274
+ // Fetch parameters from the server and populate the input fields
1275
+ async function fetchParams() {
1276
+ const response = await fetch('/params');
1277
+ const data = await response.json();
1278
+
1279
+ document.getElementById('prompt').value = data.generation_params.prompt;
1280
+ document.getElementById('neg_prompt').value = data.generation_params.negative_prompt;
1281
+ document.getElementById('width').value = data.generation_params.width;
1282
+ document.getElementById('height').value = data.generation_params.height;
1283
+ document.getElementById('cfg_scale').value = data.generation_params.cfg_scale;
1284
+ document.getElementById('guidance').value = data.generation_params.guidance;
1285
+ document.getElementById('steps').value = data.generation_params.sample_steps;
1286
+ document.getElementById('sample_method').value = data.generation_params.sample_method;
1287
+ document.getElementById('seed').value = data.generation_params.seed;
1288
+ document.getElementById('batch_count').value = data.generation_params.batch_count;
1289
+ }
1290
+
1291
+ // Call the function to fetch and populate the input fields
1292
+ fetchParams();
1293
+
1294
+ async function generateImage() {
1295
+ const prompt = document.getElementById('prompt').value;
1296
+ const neg_prompt = document.getElementById('neg_prompt').value;
1297
+ const width = document.getElementById('width').value;
1298
+ const height = document.getElementById('height').value;
1299
+ const cfg_scale = document.getElementById('cfg_scale').value;
1300
+ const steps = document.getElementById('steps').value;
1301
+ const guidance = document.getElementById('guidance').value;
1302
+ const sample_method = document.getElementById('sample_method').value;
1303
+ const seed = document.getElementById('seed').value;
1304
+ const batch_count = document.getElementById('batch_count').value;
1305
+ const canvas = document.getElementById('imageCanvas');
1306
+ const ctx = canvas.getContext('2d');
1307
+ const downloadLink = document.getElementById('downloadLink');
1308
+
1309
+ const requestBody = {
1310
+ prompt: prompt,
1311
+ negative_prompt: neg_prompt,
1312
+ ...(width && { width: parseInt(width) }),
1313
+ ...(height && { height: parseInt(height) }),
1314
+ ...(cfg_scale && { cfg_scale: parseFloat(cfg_scale) }),
1315
+ ...(steps && { steps: parseInt(steps) }),
1316
+ ...(guidance && { guidance: parseFloat(guidance) }),
1317
+ ...(sample_method && { sample_method: sample_method }),
1318
+ ...(seed && { seed: parseInt(seed) }),
1319
+ ...(batch_count && { batch_count: parseInt(batch_count) })
1320
+ };
1321
+
1322
+ const response = await fetch('/txt2img', {
1323
+ method: 'POST',
1324
+ headers: {
1325
+ 'Content-Type': 'application/json'
1326
+ },
1327
+ body: JSON.stringify(requestBody)
1328
+ });
1329
+
1330
+ const data = await response.json();
1331
+ const taskId = data.task_id;
1332
+
1333
+ let status = '';
1334
+ while (status !== 'Done' && status !== 'Failed') {
1335
+ const statusResponse = await fetch(`/result?task_id=${taskId}`);
1336
+ const statusData = await statusResponse.json();
1337
+ status = statusData.status;
1338
+
1339
+ if (status === 'Done' || status === 'Working' && statusData.data.length > 0 ) {
1340
+ const imageData = statusData.data[0].data;
1341
+ const width = statusData.data[0].width;
1342
+ const height = statusData.data[0].height;
1343
+
1344
+ const img = new Image();
1345
+ img.src = `data:image/png;base64,${imageData}`;
1346
+ img.onload = () => {
1347
+ canvas.width = width;
1348
+ canvas.height = height;
1349
+ ctx.drawImage(img, 0, 0, width, height);
1350
+ downloadLink.href = img.src;
1351
+ downloadLink.style.display = 'inline-block';
1352
+ };
1353
+ } else if (status === 'Failed') {
1354
+ alert('Image generation failed');
1355
+ }
1356
+
1357
+ await new Promise(resolve => setTimeout(resolve, 250));
1358
+ }
1359
+ }
1360
+ document.querySelectorAll('.prompt-input,.param-input').forEach(input => {
1361
+ input.addEventListener('keydown', function(event) {
1362
+ if (event.key === 'Enter') {
1363
+ event.preventDefault();
1364
+ generateImage();
1365
+ }
1366
+ });
1367
+ });
1368
+ </script>
1369
+ </body>
1370
+ </html>
1371
+ )xxx" ;
1372
+ res.set_content (html_content, " text/html" );
1373
+ } catch (const std::exception & e) {
1374
+ res.set_content (" Error loading page" , " text/plain" );
1076
1375
}
1077
1376
});
1078
1377
0 commit comments