FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
llama.cpp/tools/server/server-context.cpp at master · rioscode/llama.cpp · GitHub
llama.cpp/tools/server/server-context.cpp at master · rioscode/llama.cpp · GitHub
Skip to content
Navigation Menu
Sign in
Appearance settings
AI CODE CREATION
GitHub Copilot
Write better code with AI
GitHub Copilot app
Direct agents from issue to merge
MCP Registry
Integrate external tools
DEVELOPER WORKFLOWS
Actions
Automate any workflow
Codespaces
Instant dev environments
Issues
Plan and track work
Code Review
Manage code changes
Code Quality
Enforce quality at merge
APPLICATION SECURITY
GitHub Advanced Security
Find and fix vulnerabilities
Code security
Secure your code as you build
Secret protection
Stop leaks before they start
EXPLORE
Why GitHub
Documentation
Blog
Changelog
Marketplace
View all features
BY COMPANY SIZE
Enterprises
Small and medium teams
Startups
Nonprofits
BY USE CASE
App Modernization
DevSecOps
DevOps
CI/CD
View all use cases
BY INDUSTRY
Healthcare
Financial services
Manufacturing
Government
View all industries
View all solutions
EXPLORE BY TOPIC
AI
Software Development
DevOps
Security
View all topics
EXPLORE BY TYPE
Customer stories
Events & webinars
Ebooks & reports
Business insights
GitHub Skills
SUPPORT & SERVICES
Documentation
Customer support
Community forum
Trust center
Partners
View all resources
COMMUNITY
GitHub Sponsors
Fund open source developers
PROGRAMS
Security Lab
Maintainer Community
GitHub Stars
Archive Program
REPOSITORIES
Topics
Trending
Collections
ENTERPRISE SOLUTIONS
Enterprise platform
AI-powered developer platform
AVAILABLE ADD-ONS
GitHub Advanced Security
Enterprise-grade security features
Copilot for Business
Enterprise-grade AI features
Premium Support
Enterprise-grade 24/7 support
Pricing
Sign in
Sign up
Appearance settings
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
rioscode
/
llama.cpp
Public
forked from
ggml-org/llama.cpp
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Pull requests
0
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Pull requests
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
llama.cpp
/
tools
/
server
/
server-context.cpp
Copy path
More file actions
More file actions
Latest commit
History
History
History
5558 lines (4576 loc) · 221 KB
Breadcrumbs
llama.cpp
/
tools
/
server
/
server-context.cpp
Copy path
File metadata and controls
5558 lines (4576 loc) · 221 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#
include
"
server-context.h
"
#
include
"
server-chat.h
"
#
include
"
server-common.h
"
#
include
"
server-http.h
"
#
include
"
server-task.h
"
#
include
"
server-queue.h
"
#
include
"
server-schema.h
"
#
include
"
server-stream.h
"
#
include
"
build-info.h
"
#
include
"
common.h
"
#
include
"
fit.h
"
#
include
"
llama.h
"
#
include
"
log.h
"
#
include
"
sampling.h
"
#
include
"
speculative.h
"
#
include
"
mtmd.h
"
#
include
"
mtmd-helper.h
"
#
include
<
algorithm
>
#
include
<
cstddef
>
#
include
<
cinttypes
>
#
include
<
exception
>
#
include
<
memory
>
#
include
<
filesystem
>
#
include
<
random
>
#
include
<
utility
>
#
include
<
fstream
>
//
fix problem with std::min and std::max
#
if
defined(_WIN32)
#
define
WIN32_LEAN_AND_MEAN
#
ifndef
NOMINMAX
#
define
NOMINMAX
#
endif
#
include
<
windows.h
>
#
endif
constexpr
int
HTTP_POLLING_SECONDS
=
1
;
static
common_speculative_output_limits
server_output_limits
(
const
common_params & params) {
if
(params.
embedding
||
(params.
pooling_type
!=
LLAMA_POOLING_TYPE_UNSPECIFIED
&& params.
pooling_type
!=
LLAMA_POOLING_TYPE_NONE
)) {
return
{ params.
n_batch
,
1
};
}
auto
result =
common_speculative_get_output_limits
(
params.
n_batch
, params.
n_parallel
,
common_speculative_n_max
(¶ms.
speculative
));
result.
total
= std::max<
int32_t
>(
1
, result.
total
);
result.
per_seq
= std::max<
int32_t
>(
1
, result.
per_seq
);
return
result;
}
//
synthetic draft verification for benchmarking - accept draft tokens at random instead of by match with the target
//
on replay the draft was already accepted before a context checkpoint restore, so repeat the same decisions
static
std::vector<llama_token>
server_sample_and_accept_synth
(
common_sampler * smpl,
llama_context * ctx,
const
std::vector<
int32_t
> & idxs,
const
llama_tokens & draft,
const
std::vector<
double
> & synth_probs,
std::mt19937 & rng,
bool
is_replay) {
GGML_ASSERT
(idxs.
size
() == draft.
size
() +
1
);
GGML_ASSERT
(synth_probs.
size
() >= draft.
size
());
std::vector<llama_token> result;
result.
reserve
(idxs.
size
());
const
llama_vocab * vocab =
llama_model_get_vocab
(
llama_get_model
(ctx));
std::uniform_real_distribution<
double
>
dist
(
0.0
,
1.0
);
for
(
size_t
i =
0
; i < draft.
size
(); ++i) {
const
llama_token id =
common_sampler_sample
(smpl, ctx, idxs[i]);
const
bool
accept = is_replay ||
dist
(rng) < synth_probs[i];
//
do not accept a drafted EOG token - it would end the generation early
//
on replay the last token is from the target and can be EOG, so skip this check
if
(accept && (is_replay || !
llama_vocab_is_eog
(vocab, draft[i]))) {
//
synthetic draft tokens do not advance grammar or reasoning state
//
the last replay token is from the target and must advance both
const
bool
is_replay_target = is_replay && i +
1
== draft.
size
();
common_sampler_accept
(smpl, draft[i], is_replay_target);
result.
push_back
(draft[i]);
continue
;
}
common_sampler_accept
(smpl, id,
true
);
result.
push_back
(id);
return
result;
}
const
llama_token id =
common_sampler_sample
(smpl, ctx, idxs[draft.
size
()]);
common_sampler_accept
(smpl, id,
true
);
result.
push_back
(id);
return
result;
}
//
state diagram: https://github.com/ggml-org/llama.cpp/pull/9283
enum
slot_state {
SLOT_STATE_IDLE
,
SLOT_STATE_WAIT_OTHER
,
//
after assigning a task, but waiting for parent slot to process prompt
SLOT_STATE_STARTED
,
//
after assigning a task and about to process prompt
SLOT_STATE_PROCESSING_PROMPT
,
SLOT_STATE_DONE_PROMPT
,
SLOT_STATE_GENERATING
,
};
struct
server_slot
;
//
forward declaration
struct
server_batch
{
llama_batch batch;
bool
batch_rendered =
false
;
struct
token
{
int32_t
id_slot;
llama_token token;
llama_pos pos;
bool
output;
bool
is_prompt;
//
for stats tracking
};
std::vector<token> tokens;
int32_t
n_tokens_alloc =
0
;
int32_t
n_embd =
0
;
//
track if given slot can be batched with slots already in the batch
server_slot * slot_batched =
nullptr
;
//
in embd mode, we temporarily swap out the tokens arr and restore it on clear()
bool
has_embd =
false
;
llama_token * tokens_ptr =
nullptr
;
std::vector<
float
> embd;
float
alora_scale = -
1
.
0f
;
size_t
alora_disabled_id =
0
;
server_batch
() {
batch.
pos
=
nullptr
;
//
sentinel: uninitialized batch
}
~server_batch
() {
if
(batch.
pos
!=
nullptr
) {
clear
();
llama_batch_free
(batch);
}
}
void
init
(
int32_t
n_tokens_alloc,
int32_t
n_embd) {
this
->
n_tokens_alloc
= n_tokens_alloc;
this
->
n_embd
= n_embd;
batch =
llama_batch_init
(n_tokens_alloc,
0
,
1
);
tokens_ptr = batch.
token
;
tokens.
reserve
(n_tokens_alloc);
}
bool
add
(
int32_t
id_slot, llama_token token, llama_pos pos,
bool
output,
bool
is_prompt) {
GGML_ASSERT
(!has_embd);
//
cannot mix tokens + embd in same batch
GGML_ASSERT
(batch.
pos
!=
nullptr
);
if
((
int32_t
)tokens.
size
() >= n_tokens_alloc) {
return
false
;
}
tokens.
push_back
({ id_slot, token, pos, output, is_prompt });
return
true
;
}
bool
add
(
int32_t
id_slot,
const
std::vector<
float
> & embd_in, llama_pos pos,
bool
output,
bool
is_prompt) {
GGML_ASSERT
(batch.
pos
!=
nullptr
);
if
((
int32_t
)tokens.
size
() >= n_tokens_alloc) {
return
false
;
}
tokens.
push_back
({ id_slot,
LLAMA_TOKEN_NULL
, pos, output, is_prompt });
has_embd =
true
;
embd.
insert
(embd.
end
(), embd_in.
begin
(), embd_in.
end
());
return
true
;
}
void
clear
() {
tokens.
clear
();
embd.
clear
();
common_batch_clear
(batch);
slot_batched =
nullptr
;
alora_scale = -
1
.
0f
;
alora_disabled_id =
0
;
batch_rendered =
false
;
has_embd =
false
;
if
(batch.
token
==
nullptr
) {
batch.
token
= tokens_ptr;
batch.
embd
=
nullptr
;
}
}
int32_t
size
()
const
{
return
(
int32_t
)tokens.
size
();
}
void
set_output
(
int32_t
idx,
bool
output) {
GGML_ASSERT
(idx >=
0
&& idx < (
int32_t
)tokens.
size
());
tokens[idx].
output
= output;
}
void
render
() {
GGML_ASSERT
(!batch_rendered);
GGML_ASSERT
(batch.
pos
!=
nullptr
);
common_batch_clear
(batch);
for
(
int32_t
i =
0
; i <
size
(); i++) {
const
auto
& t = tokens[i];
common_batch_add
(batch, t.
token
, t.
pos
, { t.
id_slot
}, t.
output
);
}
if
(has_embd) {
batch.
token
=
nullptr
;
//
will be restored on clear()
batch.
embd
= embd.
data
();
}
batch_rendered =
true
;
}
llama_batch
get_view
(
int32_t
off,
int32_t
n_tokens)
const
{
GGML_ASSERT
(batch.
pos
!=
nullptr
);
GGML_ASSERT
(batch_rendered);
GGML_ASSERT
(off >=
0
&& off <
size
());
GGML_ASSERT
(n_tokens >
0
&& off + n_tokens <=
size
());
auto
* token = batch.
token
? batch.
token
+ off :
nullptr
;
auto
* embd = batch.
embd
? batch.
embd
+ off * n_embd :
nullptr
;
llama_batch view = {
n_tokens,
token,
embd,
batch.
pos
+ off,
batch.
n_seq_id
+ off,
batch.
seq_id
+ off,
batch.
logits
+ off,
};
return
view;
}
};
struct
server_slot
{
int
id;
llama_context * ctx_tgt =
nullptr
;
llama_context * ctx_dft =
nullptr
;
common_memory mem;
//
multimodal
mtmd_context * mctx =
nullptr
;
mtmd::batch_ptr mbatch =
nullptr
;
//
speculative decoding
common_speculative * spec;
llama_tokens spec_draft;
llama_tokens spec_prompt;
std::vector<
int32_t
> spec_i_batch;
common_prompt_checkpoint spec_ckpt;
bool
spec_is_replay =
false
;
std::mt19937 spec_synth_rng;
//
TODO: move members that belong to the task (such as `generated_text`, `has_new_line`) to task_results_state
//
see https://github.com/ggml-org/llama.cpp/pull/18283#issuecomment-3710175837
std::unique_ptr<
const
server_task> task;
std::unique_ptr<
const
server_task> task_prev;
//
used for debugging
//
used to determine the slot that has been used the longest
int64_t
t_last_used = -
1
;
//
generation props
int32_t
n_ctx =
0
;
//
context size per slot
int32_t
n_keep =
0
;
int32_t
i_batch = -
1
;
//
effective generation limit for the current task, -1 means unlimited
int32_t
n_predict_max = -
1
;
size_t
last_nl_pos =
0
;
std::string generated_text;
std::string debug_generated_text;
llama_tokens generated_tokens;
size_t
n_sent_text =
0
;
//
number of sent text character (i.e. handle partial UTF-8 on streaming)
std::vector<completion_token_output> generated_token_probs;
bool
has_next_token =
true
;
bool
has_new_line =
false
;
bool
truncated =
false
;
stop_type stop;
std::string stopping_word;
//
state
slot_state state =
SLOT_STATE_IDLE
;
server_prompt prompt;
bool
prompt_save
(server_prompt_cache & prompt_cache)
const
{
if
(prompt.
tokens
.
size
() ==
0
) {
return
false
;
}
const
size_t
cur_size_tgt =
llama_state_seq_get_size_ext
(ctx_tgt, id,
LLAMA_STATE_SEQ_FLAGS_NONE
);
const
size_t
cur_size_dft = ctx_dft ?
llama_state_seq_get_size_ext
(ctx_dft, id,
LLAMA_STATE_SEQ_FLAGS_NONE
) :
0
;
const
size_t
cur_size = cur_size_tgt + cur_size_dft;
SRV_TRC
(
"
- saving prompt with length %d, total state size = %.3f MiB (draft: %.3f MiB)
\n
"
,
(
int
) prompt.
tokens
.
size
(), cur_size / (
1024.0
*
1024.0
), cur_size_dft / (
1024.0
*
1024.0
));
auto
* cur = prompt_cache.
alloc
(prompt, cur_size_tgt, cur_size_dft);
if
(cur ==
nullptr
) {
return
false
;
}
llama_state_seq_get_data_ext
(ctx_tgt, cur->
data
.
main
.
data
(), cur_size_tgt, id,
LLAMA_STATE_SEQ_FLAGS_NONE
);
if
(ctx_dft) {
llama_state_seq_get_data_ext
(ctx_dft, cur->
data
.
drft
.
data
(), cur_size_dft, id,
LLAMA_STATE_SEQ_FLAGS_NONE
);
}
return
true
;
}
bool
prompt_load
(server_prompt_cache & prompt_cache,
const
server_tokens & tokens) {
bool
res = prompt_cache.
load
(prompt, tokens, ctx_tgt, ctx_dft, id);
if
(!res) {
SLT_WRN
(*
this
,
"
%s
"
,
"
failed to load prompt from cache
\n
"
);
}
return
res;
}
void
prompt_clear
() {
SLT_TRC
(*
this
,
"
clearing prompt with %zu tokens
\n
"
, prompt.
tokens
.
size
());
mem.
seq_rm
(id, -
1
, -
1
);
prompt.
clear
();
}
std::vector<common_adapter_lora_info> lora;
int32_t
alora_invocation_start = -
1
;
//
sampling
json json_schema;
common_sampler_ptr smpl;
llama_token sampled;
//
in speculative mode, this is the last accepted token
//
for TTS models, this is the embd generated from prev step, decode this to generate next hidden state
//
corresponding to one token position (size = n_embd)
std::vector<
float
> inp_embd;
server_slot_stats stats;
//
accepted tokens per draft position
//
not in server_slot_stats to avoid copying to every task result
std::vector<
uint64_t
> n_accepted_per_pos;
std::function<
void
(
int
/*
id_slot
*/
)> callback_on_release;
std::function<
void
(
const
server_slot &)> callback_on_reset;
//
called before reset()
//
this is for printing timings with slot progress, not part of metrics
int64_t
t_print_last =
0
;
int32_t
n_gen_last =
0
;
void
reset
() {
SLT_DBG
(*
this
,
"
%s
"
,
"
\n
"
);
spec_is_replay =
false
;
last_nl_pos =
0
;
generated_text =
"
"
;
has_new_line =
false
;
truncated =
false
;
stop =
STOP_TYPE_NONE
;
stopping_word =
"
"
;
n_sent_text =
0
;
if
(
can_speculate
()) {
spec_draft.
clear
();
spec_i_batch.
clear
();
spec_ckpt.
clear
();
}
generated_tokens.
clear
();
generated_token_probs.
clear
();
json_schema =
json
();
task_prev =
std::move
(task);
task.
reset
();
//
note: callback_on_reset() must have run before this, see release()
stats = {};
n_accepted_per_pos.
clear
();
n_predict_max = -
1
;
llama_set_sampler
(ctx_tgt, id,
nullptr
);
//
clear alora start
alora_invocation_start = -
1
;
//
clear multimodal state
mbatch.
reset
();
}
void
init_sampler
()
const
{
common_sampler_reset
(smpl.
get
());
if
(!task->
need_sampling
()) {
return
;
}
const
int64_t
t_start =
ggml_time_us
();
int
n_text =
0
;
for
(
int
i =
0
; i < (
int
) prompt.
tokens
.
size
(); i++) {
const
llama_token id = prompt.
tokens
[i];
if
(id !=
LLAMA_TOKEN_NULL
) {
common_sampler_accept
(smpl.
get
(), id,
false
);
n_text++;
}
}
SLT_TRC
(*
this
,
"
init sampler, took %0.2f ms, tokens: text = %d, total = %d
\n
"
,
(
ggml_time_us
() - t_start) /
1000.0
, n_text, (
int
) prompt.
tokens
.
size
());
}
bool
need_embd
()
const
{
GGML_ASSERT
(task);
return
task->
need_embd
();
}
//
if the context does not have a memory module then all embeddings have to be computed within a single ubatch
//
also we cannot split if the pooling would require any past tokens
//
(MTP supports splitting — uses task->need_embd() not need_embd())
bool
can_split
()
const
{
GGML_ASSERT
(task);
return
!task->
need_embd
() ||
(
llama_get_memory
(ctx_tgt) &&
llama_pooling_type
(ctx_tgt) ==
LLAMA_POOLING_TYPE_LAST
);
}
bool
can_batch_with
(server_slot & other_slot)
const
{
GGML_ASSERT
(task);
return
task->
type
== other_slot.
task
->
type
&& inp_embd.
size
() == other_slot.
inp_embd
.
size
()
&&
are_lora_equal
(lora, other_slot.
lora
);
}
//
returns -1 if the generation is limitless
int32_t
n_remaining
()
const
{
return
n_predict_max == -
1
? -
1
: n_predict_max - (
int32_t
) stats.
n_gen
;
}
bool
has_budget
()
const
{
return
n_predict_max == -
1
||
n_remaining
() >
0
;
}
bool
is_processing
()
const
{
return
state !=
SLOT_STATE_IDLE
;
}
bool
can_speculate
()
const
{
return
!!spec;
}
void
add_token
(
const
completion_token_output & token) {
if
(!
is_processing
()) {
SLT_WRN
(*
this
,
"
%s
"
,
"
slot is not processing
\n
"
);
return
;
}
generated_token_probs.
push_back
(token);
}
int
get_n_draft_max
()
const
{
GGML_ASSERT
(task);
if
(!
can_speculate
()) {
return
0
;
}
//
determine the max draft that fits the current slot state
//
note: slot.prompt is not yet expanded with the `id` token sampled above
//
also, need to leave space for 1 extra token to allow context shifts
int
n_draft_max = n_ctx - prompt.
n_tokens
() -
2
;
if
(
n_remaining
() >
0
) {
n_draft_max =
std::min
(n_draft_max,
n_remaining
() -
1
);
}
SLT_DBG
(*
this
,
"
max possible draft: %d
\n
"
, n_draft_max);
return
n_draft_max;
}
//
add sampled token of this slot to the batch, optionally add the speculative draft tokens if any
void
handle_last_sampled_token
(server_batch & batch) {
bool
add_ok =
true
;
if
(spec_draft.
empty
()) {
//
no speculative decoding
i_batch = batch.
size
();
if
(!inp_embd.
empty
()) {
add_ok &= batch.
add
(id, inp_embd, prompt.
tokens
.
pos_next
(),
true
,
false
);
}
else
{
add_ok &= batch.
add
(id, sampled, prompt.
tokens
.
pos_next
(),
true
,
false
);
}
SLT_DBG
(*
this
,
"
slot decode token, id=%d, n_ctx = %d, n_tokens = %d, truncated = %d
\n
"
,
sampled, n_ctx, prompt.
n_tokens
(), truncated);
}
else
{
SLT_DBG
(*
this
,
"
generate_draft: id=%d, #tokens=%zu, #draft=%zu, pos_next=%d
\n
"
,
sampled, prompt.
tokens
.
size
(), spec_draft.
size
(), prompt.
tokens
.
pos_next
());
GGML_ASSERT
(spec_i_batch.
empty
());
spec_i_batch.
push_back
(batch.
size
());
for
(
size_t
i =
0
; i < spec_draft.
size
(); i++) {
spec_i_batch.
push_back
(batch.
size
() + i +
1
);
}
auto
pos0 = prompt.
tokens
.
pos_next
();
add_ok &= batch.
add
(id, sampled, pos0++,
true
,
false
);
for
(
auto
token : spec_draft) {
add_ok &= batch.
add
(
this
->
id
, token, pos0++,
true
,
false
);
}
}
GGML_ASSERT
(add_ok &&
"
batch must be large enough to hold the sampled and draft tokens
"
);
prompt.
tokens
.
push_back
(sampled);
prompt.
tokens
.
insert
(spec_draft);
}
void
release
() {
if
(
is_processing
()) {
GGML_ASSERT
(task);
SLT_INF
(*
this
,
"
stop processing: n_tokens = %d, truncated = %d
\n
"
, prompt.
n_tokens
(), truncated);
t_last_used =
ggml_time_us
();
state =
SLOT_STATE_IDLE
;
//
do not keep context of the child slots - the parent's context is enough
if
(task->
is_child
()) {
prompt_clear
();
}
callback_on_reset
(*
this
);
reset
();
callback_on_release
(id);
}
}
size_t
find_stopping_strings
(
const
std::string & text,
const
size_t
last_token_size,
bool
is_full_stop) {
GGML_ASSERT
(task);
size_t
stop_pos = std::string::npos;
for
(
const
std::string & word : task->
params
.
antiprompt
) {
size_t
pos;
if
(is_full_stop) {
const
size_t
tmp = word.
size
() + last_token_size;
const
size_t
from_pos = text.
size
() > tmp ? text.
size
() - tmp :
0
;
pos = text.
find
(word, from_pos);
}
else
{
//
otherwise, partial stop
pos =
string_find_partial_stop
(text, word);
}
if
(pos != std::string::npos && (stop_pos == std::string::npos || pos < stop_pos)) {
if
(is_full_stop) {
stop =
STOP_TYPE_WORD
;
stopping_word = word;
has_next_token =
false
;
}
stop_pos = pos;
}
}
return
stop_pos;
}
void
print_timings_tg
() {
if
(stats.
n_gen
<
100
) {
return
;
}
const
int64_t
t_now =
ggml_time_us
();
if
(t_now - t_print_last <
3
*
1000
*
1000
) {
return
;
}
const
double
n_gen_second = stats.
n_gen_tps
();
const
double
n_gen_second_win =
1e6
/ (t_now - t_print_last) * (stats.
n_gen
- n_gen_last);
t_print_last = t_now;
n_gen_last = stats.
n_gen
;
SLT_INF
(*
this
,
"
n_gen = %6d, tg = %6.2f t/s, tg_3s = %6.2f t/s
\n
"
, (
int
) stats.
n_gen
, n_gen_second, n_gen_second_win);
}
void
print_timings_pp
()
const
{
const
double
t_prompt_total = stats.
t_prompt_ms
();
if
(t_prompt_total <
3000.0
) {
return
;
}
const
double
n_prompt_second = stats.
n_prompt_tps
();
const
double
f_progress = task->
n_tokens
() >
0
? (
double
) prompt.
n_tokens
() / task->
n_tokens
() :
0.0
;
SLT_INF
(*
this
,
"
prompt processing, n_tokens = %6d, progress = %.2f, t = %6.2f s / %.2f tokens per second
\n
"
,
(
int
) stats.
n_prompt_processed
, f_progress, t_prompt_total /
1e3
, n_prompt_second);
}
void
print_timings
()
const
{
const
double
t_prompt_total = stats.
t_prompt_ms
();
const
double
t_gen_total = stats.
t_gen_ms
();
const
double
t_prompt = stats.
t_prompt_per_token_ms
();
const
double
n_prompt_second = stats.
n_prompt_tps
();
const
double
t_gen = stats.
t_gen_per_token_ms
();
const
double
n_gen_second = stats.
n_gen_tps
();
SLT_INF
(*
this
,
"
prompt eval time = %10.2f ms / %5d tokens (%8.2f ms per token, %8.2f tokens per second)
\n
"
,
t_prompt_total, (
int
) stats.
n_prompt_processed
, t_prompt, n_prompt_second);
SLT_INF
(*
this
,
"
eval time = %10.2f ms / %5d tokens (%8.2f ms per token, %8.2f tokens per second)
\n
"
,
t_gen_total, (
int
) stats.
n_gen
, t_gen, n_gen_second);
SLT_INF
(*
this
,
"
total time = %10.2f ms / %5d tokens
\n
"
,
t_prompt_total + t_gen_total, (
int
) (stats.
n_prompt_processed
+ stats.
n_gen
));
SLT_INF
(*
this
,
"
graphs reused = %10d
\n
"
,
llama_perf_context
(ctx_tgt).
n_reused
);
const
int32_t
n_draft_total = stats.
n_draft_tokens
;
const
int32_t
n_draft_accepted = stats.
n_draft_accepted
;
const
int32_t
n_draft_verif_steps = stats.
n_draft_verif_steps
;
if
(n_draft_total >
0
) {
const
float
draft_ratio = (
float
) n_draft_accepted / n_draft_total;
const
double
mean_acc_len = n_draft_verif_steps >
0
?
1.0
+ (
double
) n_draft_accepted / (
double
) n_draft_verif_steps :
1.0
;
std::string acceptance_rates_per_pos;
if
(n_draft_verif_steps >
0
) {
for
(
size_t
i =
0
; i < n_accepted_per_pos.
size
(); ++i) {
if
(i >
0
) {
acceptance_rates_per_pos +=
"
,
"
;
}
acceptance_rates_per_pos +=
string_format
(
"
%.3f
"
, (
double
) n_accepted_per_pos[i] / (
double
) n_draft_verif_steps);
}
}
SLT_INF
(*
this
,
"
draft acceptance = %0.5f (%5d accepted / %5d generated), mean len = %5.2f
\n
"
,
draft_ratio, n_draft_accepted, n_draft_total, mean_acc_len);
SLT_TRC
(*
this
,
"
acc per pos = (%s)
\n
"
, acceptance_rates_per_pos.
c_str
());
}
common_speculative_print_stats
(spec);
}
json
to_json
(
bool
only_metrics =
false
)
const
{
json res;
res = {
{
"
id
"
, id},
{
"
n_ctx
"
, n_ctx},
{
"
speculative
"
,
can_speculate
()},
{
"
is_processing
"
,
is_processing
()},
};
const
auto
& ptask = task ? task : task_prev;
if
(ptask) {
res[
"
id_task
"
] = ptask->
id
;
res[
"
n_prompt_tokens
"
] = (
int32_t
) prompt.
tokens
.
size
();
res[
"
n_prompt_tokens_processed
"
] = stats.
n_prompt_processed
;
res[
"
n_prompt_tokens_cache
"
] = stats.
n_prompt_cached
;
res[
"
params
"
] = ptask->
params
.
to_json
(only_metrics);
res[
"
next_token
"
] =
json::array
({
{
{
"
has_next_token
"
, has_next_token},
{
"
has_new_line
"
, has_new_line},
{
"
n_remain
"
,
n_remaining
()},
{
"
n_decoded
"
, stats.
n_gen
},
}
});
if
(!only_metrics) {
res[
"
prompt
"
] = ptask->
tokens
.
detokenize
(ctx_tgt,
true
);
res[
"
generated
"
] = generated_text.
empty
() ? debug_generated_text : generated_text;
}
}
return
res;
}
void
copy_state_to
(server_slot & other)
const
{
GGML_ASSERT
(state ==
SLOT_STATE_DONE_PROMPT
);
mem.
seq_rm
(other.
id
, -
1
, -
1
);
mem.
seq_cp
(id, other.
id
, -
1
, -
1
);
other.
i_batch
= i_batch;
other.
stats
= stats;
other.
prompt
= prompt.
clone
();
other.
init_sampler
();
}
};
//
returns 0 on success
//
caller need to update prompt.tokens after a successful call to keep track of the processing progress
//
note: this is not a member of server_slot because we want to run it inside yield_to_queue
//
slot is passed as const to avoid accidental modification of the slot state
//
some pointers are allowed to be used, they are not used by to_json()
static
int
process_mtmd_chunk
(
const
server_slot & slot, mtmd::batch_ptr & mbatch,
size_t
idx,
size_t
& n_tokens_out) {
GGML_ASSERT
(slot.
mctx
);
const
auto
& mctx = slot.
mctx
;
const
auto
& input_tokens = slot.
task
->
tokens
;
const
auto
& chunk = input_tokens.
find_chunk
(idx);
int32_t
res =
0
;
auto
try_decode = [&]() ->
int32_t
{
if
(mbatch) {
float
* embd =
mtmd_batch_get_output_embd
(mbatch.
get
(), chunk.
get
());
if
(embd) {
void
* cb_data = slot.
spec
;
static
auto
cb = [](llama_batch batch,
void
* user_data) {
common_speculative * spec =
static_cast
<common_speculative *>(user_data);
if
(!
common_speculative_process
(spec, batch)) {
return
1
;
}
return
0
;
};
llama_pos new_n_past;
//
unused for now
res =
mtmd_helper_decode_image_chunk
(
mctx,
slot.
ctx_tgt
,
chunk.
get
(),
embd,
slot.
prompt
.
tokens
.
pos_next
(),
slot.
id
,
llama_n_batch
(slot.
ctx_tgt
),
&new_n_past,
cb,
cb_data
);
if
(res !=
0
) {
SLT_ERR
(slot,
"
failed to decode mtmd chunk, idx = %zu, res = %d
\n
"
, idx, res);
return
-
1
;
}
n_tokens_out =
mtmd_input_chunk_get_n_tokens
(chunk.
get
());
return
0
;
//
success
}
}
return
1
;
//
(non-error) need to create & encode batch
};
//
if the batch is already exist, try searching & encode
res =
try_decode
();
if
(res ==
0
) {
return
0
;
}
if
(res <
0
) {
//
fatal error
return
res;
}
//
otherwise, the batch is either uninitialized or is used up
//
we need to create & encode a new batch
mbatch.
reset
(
mtmd_batch_init
(mctx));
res =
mtmd_batch_add_chunk
(mbatch.
get
(), chunk.
get
());
GGML_ASSERT
(res ==
0
);
//
we should never have an empty batch
//
try batching as much as possible
int
n_added =
1
;
size_t
idx_cur = idx;
while
(res ==
0
) {
auto
[next_chunk, next_idx] = input_tokens.
find_next_media_chunk
(idx_cur);
if
(next_chunk ==
nullptr
) {
break
;
}
res =
mtmd_batch_add_chunk
(mbatch.
get
(), next_chunk->
get
());
n_added += (res ==
0
?
1
:
0
);
idx_cur = next_idx;
SLT_DBG
(slot,
"
try adding media chunk idx = %zu to batch, res = %d
\n
"
, next_idx, res);
//
if res != 0, batch is full or chunk is not compatible -> this loop breaks
}
//
TODO @ngxson : move this log line to debug when it become more stable
SLT_TRC
(slot,
"
encoding mtmd batch from idx = %zu, n_chunks = %d
\n
"
, idx, n_added);
res =
mtmd_batch_encode
(mbatch.
get
());
if
(res !=
0
) {
SLT_ERR
(slot,
"
failed to encode mtmd batch for chunk idx = %zu, res = %d
\n
"
, idx, res);
return
-
1
;
}
return
try_decode
();
}
//
//
server_context_impl (private implementation)
//
struct
server_context_impl
{
friend
struct
server_context
;
public:
//
only use these pointers outside of this class:
//
- when not in sleeping state
//
- and, with thread-safe APIs (e.g., tokenizer calls)
llama_model * model_tgt =
nullptr
;
mtmd_context * mctx =
nullptr
;
//
note: video_params.ffmpeg_bin_dir points into params_base, which outlives this struct
mtmd_helper_init_opt init_opt = mtmd_helper_init_opt_default();
const
llama_vocab * vocab =
nullptr
;
server_queue queue_tasks;
server_response queue_results;
//
note: chat_params must not be refreshed upon existing sleeping state
server_chat_params chat_params;
server_state_callback_t
callback_state = [](server_state, json) ->
void
{};
server_context_impl
() {
mtmd_helper_log_set
(common_log_default_callback,
nullptr
);
}
~server_context_impl
() {
if
(!sleeping) {
//
destroy() is already called when entering sleeping state
//
we don't call it again here to avoid double free
destroy
();
}
}
server_metrics
get_metrics
()
const
{
return
metrics;
}
void
reset_metrics_bucket
() {
metrics.
reset_bucket
();
}
private:
//
note: accessing these fields outside of this class is not thread-safe
//
use server_context methods instead
common_params params_base;
//
note: keep these alive - they determine the lifetime of the model, context, etc.
common_init_result_ptr llama_init;
llama_context * ctx_tgt =
nullptr
;
server_batch batch;
llama_model * model_dft =
nullptr
;
llama_context * ctx_dft =
nullptr
;
common_speculative_init_result_ptr spec_init;
common_context_seq_rm_type ctx_tgt_seq_rm_type =
COMMON_CONTEXT_SEQ_RM_TYPE_NO
;
common_context_seq_rm_type ctx_dft_seq_rm_type =
COMMON_CONTEXT_SEQ_RM_TYPE_NO
;
common_speculative_ptr spec;
bool
add_bos_token =
true
;
int32_t
n_ctx;
//
total context for all clients / slots
//
set to llama_model_n_swa(model)
//
if swa_full is enabled, this is set to 0 to simulate a non-SWA model
int32_t
n_swa;
//
slots / clients
std::vector<server_slot> slots;
int
trace =
0
;
//
env: LLAMA_TRACE
int
slots_debug =
0
;
//
env: LLAMA_SERVER_SLOTS_DEBUG
int
slots_n_diff =
0
;
//
env: LLAMA_SERVER_SLOTS_N_DIFF
int
n_empty_consecutive =
0
;
std::unique_ptr<server_prompt_cache> prompt_cache;
server_metrics metrics;
//
queued prompt stats - llama_decode() is async, so the timing is only valid after a sync
//
note: kept out of server_metrics, which is copied as-is into the task result
int64_t
t_decode_start =
0
;
//
start of the last submitted decode
int64_t
t_prompt_start =
0
;
//
start of the oldest queued prompt decode
uint64_t
n_prompt_queued =
0
;
json json_ui_settings = json::object();
//
Necessary similarity of prompt for slot selection
float
slot_prompt_similarity =
0
.
0f
;
std::string model_name;
//
name of the loaded model, to be used by API
std::set<std::string> model_aliases;
//
additional names for the model
std::set<std::string> model_tags;
//
informational tags
bool
sleeping =
false
;
int64_t
t_last_load_progress_ms =
0
;
void
destroy
() {
spec.
reset
();
spec_init.
reset
();
ctx_dft =
nullptr
;
model_dft =
nullptr
;
llama_init.
reset
();
ctx_tgt =
nullptr
;
model_tgt =
nullptr
;
mtmd_free
(mctx);
mctx =
nullptr
;
}
void
handle_sleeping_state
(
bool
new_state) {
GGML_ASSERT
(sleeping != new_state);
if
(new_state) {
if
(callback_state) {
callback_state
(
SERVER_STATE_SLEEPING
, {});
//
note: for sleeping == false, event is emitted by load_model()
}
SRV_INF
(
"
%s
"
,
"
server is entering sleeping state
\n
"
);
destroy
();
}
else
{
SRV_INF
(
"
%s
"
,
"
server is exiting sleeping state
\n
"
);
if
(!
load_model
(params_base)) {
GGML_ABORT
(
"
failed to reload model after sleeping
"
);
}
}
sleeping = new_state;
}
struct
load_progress_data
{
server_context_impl * ctx;
std::string stage;
std::vector<std::string> stages;
int64_t
t_last_load_progress_ms =
0
;
load_progress_data
(server_context_impl * ctx,
const
std::string & stage) : ctx(ctx), stage(stage) {}
};
static
bool
load_progress_callback
(
float
progress,
void
* user_data) {
auto
* d =
static_cast
<load_progress_data *>(user_data);
GGML_ASSERT
(d);
//
always emit the first and final sample; throttle the rest to one per 200ms
{
auto
& t_last = d->
t_last_load_progress_ms
;
const
int64_t
t_now =
ggml_time_ms
();
const
bool
first = t_last ==
0
;
const
bool
done = progress >=
1
.
0f
;
const
bool
throttled = !first && !done && (t_now - t_last) <
200
;
if
(throttled) {
return
true
;
}
t_last = t_now;
}
if
(d->
ctx
->
callback_state
) {
d->
ctx
->
callback_state
(
SERVER_STATE_LOADING
, {
{
"
stages
"
, d->
stages
},
{
"
current
"
, d->
stage
},
{
"
value
"
, progress},
});
}
View remainder of file in raw view
Footer
© 2026 GitHub, Inc.
Footer navigation
Terms
Privacy
Security
Status
Community
Docs
Contact
You can’t perform that action at this time.
Back
|
FazBrowse Home
|
New Git URL