| 11245 | } |
| 11246 | |
| 11247 | static int server_multimodal_resume_pos(ds4_session *session, |
| 11248 | const ds4_tokens *prompt, |
| 11249 | const ds4_vision_span *images, |
| 11250 | size_t image_count) { |
| 11251 | if (!session || !prompt) return 0; |
| 11252 | const int live = ds4_session_pos(session); |
| 11253 | const int common = ds4_session_common_prefix(session, prompt); |
| 11254 | return server_multimodal_resume_frontier( |
| 11255 | live, common, prompt->len, |
| 11256 | ds4_session_vision_state_matches(session, images, image_count)); |
| 11257 | } |
| 11258 | |
| 11259 | static int server_session_sync_multimodal(server *s, server_slot *slot, |
| 11260 | const ds4_tokens *prompt, |
| 11261 | const ds4_vision_span *images, |
| 11262 | size_t image_count, |
| 11263 | char *err, size_t errlen) { |
| 11264 | if (!image_count) |
| 11265 | return server_session_sync(s, slot, prompt, err, errlen); |
| 11266 | if (!s || !slot || !prompt || !images) return 1; |
| 11267 | if (!s->batched_mode) { |
| 11268 | if (!server_prefill_enter(s, slot)) return DS4_SESSION_SYNC_INTERRUPTED; |
| 11269 | int rc = ds4_session_sync_multimodal(slot->session, prompt, |
| 11270 | images, image_count, |
| 11271 | err, errlen); |
| 11272 | server_prefill_leave(s); |
| 11273 | return rc; |
| 11274 | } |
| 11275 | |
| 11276 | /* Start at the exact live frontier when both tokens and image identities |
| 11277 | * match. Starting at zero made the first scheduling slice truncate a |
| 11278 | * perfectly reusable long checkpoint, forcing a complete refill. */ |
| 11279 | pthread_mutex_lock(&s->inference_mu); |
| 11280 | int done = server_multimodal_resume_pos(slot->session, prompt, |
| 11281 | images, image_count); |
| 11282 | pthread_mutex_unlock(&s->inference_mu); |
| 11283 | bool called = false; |
| 11284 | while (!g_stop_requested && !slot_job_cancelled(slot) && |
| 11285 | (!called || done < prompt->len)) { |
| 11286 | int quantum = server_prefill_quantum(s); |
| 11287 | int target = done + quantum; |
| 11288 | if (target > prompt->len || target < done) target = prompt->len; |
| 11289 | if (target <= 0) target = prompt->len; |
| 11290 | for (size_t i = 0; i < image_count; i++) { |
| 11291 | uint64_t end = (uint64_t)images[i].token_start + |
| 11292 | images[i].embedding.token_count; |
| 11293 | if ((uint64_t)target > images[i].token_start && |
| 11294 | (uint64_t)target < end) { |
| 11295 | target = end > (uint64_t)prompt->len ? prompt->len : (int)end; |
| 11296 | } |
| 11297 | } |
| 11298 | size_t prefix_images = 0; |
| 11299 | while (prefix_images < image_count) { |
| 11300 | uint64_t end = (uint64_t)images[prefix_images].token_start + |
| 11301 | images[prefix_images].embedding.token_count; |
| 11302 | if (end > (uint64_t)target) break; |
| 11303 | prefix_images++; |
| 11304 | } |
nothing calls this directly
no test coverage detected