ember.cpp 0.1.0
C++ inference engine for Llama-family and paperkiln models (CPU, CUDA, WebAssembly)
Loading...
Searching...
No Matches
api.h
Go to the documentation of this file.
1#ifndef TINYLLAMA_API_H
2#define TINYLLAMA_API_H
3
4#include <chrono>
5#include <functional>
6#include <map>
7#include <memory>
8#include <stdexcept>
9#include <string>
10#include <vector>
11#include <random>
12#include <sstream>
13
14#include "chat_tokenizer.h"
15#include "state_tokens.h"
16#include "model.h"
17#include "tokenizer.h"
18
19struct KVCache;
20namespace ember_ir { class Interpreter; }
21
22namespace tinyllama {
23
38 std::chrono::steady_clock::time_point t_entry; // generate() entered
39 std::chrono::steady_clock::time_point t_first_token; // first token emitted
41 bool first_token = false; // false: generate() bailed out before emitting one
42 bool batch_prefill = false; // false: token-by-token path (short prompts / CPU)
43 double tokenize_ms = 0.0; // template + encode
44 double kv_reset_ms = 0.0; // kv_cache_.clear_data()
45 double embed_ms = 0.0; // host embedding lookup for the whole prompt
46 double prefill_wall_ms = 0.0; // embeddings -> last-token logits on host: CPU-offload
47 // layers, device alloc + upload, GPU forward, free
48 double prefill_gpu_span_ms = -1.0; // CUDA-event span of that call; -1 = not measured
49 double sample_ms = 0.0; // repetition penalty + sampling + first-token decode
50 double ttft_ms = 0.0; // t_entry -> t_first_token
51
52 // Prefix-cache opportunity (logged only; the KV cache is still cleared).
53 // The previous request left prompt + generated[:-1] in the KV cache; the
54 // reusable prefix is its longest common prefix with this prompt, capped at
55 // num_prompt_tokens - 1 because the last prompt token must be computed to
56 // produce logits. hit rate = reusable / num_prompt_tokens.
59 double prefix_hit_rate = 0.0;
60 int prefix_reused_tokens = 0; // actually reused from the KV cache (not recomputed)
61 // Speculative decoding (EMBER_SPEC=k): draft tokens proposed, accepted, and
62 // verification passes run (each pass replaces 1 + accepted decode steps).
63 int spec_drafted = 0;
65 int spec_passes = 0;
66 // The drafter's own time (building the history, prompt lookup, the EMBER_DRAFT=table walk) and its calls,
67 // one per decode position it was asked at; 0 when speculation is off.
68 double spec_draft_ms = 0.0;
70
71 // Per-op GPU time of the batched prefill (EMBER_OP_TIMING=1 only; empty
72 // otherwise): op name -> ms over all layers, plus gpu_busy_in_ops,
73 // gpu_idle_between_ops and gpu_span. See PrefillOpTimer in model.cpp.
74 std::map<std::string, double> op_ms;
75};
76
86 public:
101 TinyLlamaSession(const std::string& model_path,
102 const std::string& tokenizer_path, int threads = 1,
103 int num_gpu_layers_from_cli = 0, bool cli_use_mmap = true,
104 bool use_kv_quant = false, bool use_batch_generation = false,
105 int max_batch_size = 1);
106
111
132 std::string generate(const std::string& prompt, int steps = 128,
133 float temperature = 0.1f,
134 int top_k = 40,
135 float top_p = 0.9f,
136 const std::string& system_prompt = "",
137 bool apply_q_a_format = false);
138
156 std::vector<std::string> generate_batch(const std::vector<std::string>& prompts,
157 int steps = 128,
158 float temperature = 0.1f,
159 int top_k = 40,
160 float top_p = 0.9f,
161 const std::string& system_prompt = "",
162 bool apply_q_a_format = false);
163
164 const Tokenizer* get_tokenizer() const { return tokenizer_.get(); }
165 const ModelConfig& get_config() const { return config_; }
167
168 // Repetition penalty applied to recently generated tokens during sampling
169 // (CTRL-style; 1.0 disables). Default 1.3: tiny word-level models loop
170 // hard without it.
173
174 // The /chat contract's repetition penalty (LOCAL_LLM_STACK.md section 3,
175 // point 4), separate from the CTRL-style one above: over the last `last_n`
176 // ids the next token conditions on (prompt + reply so far), each distinct id
177 // outside the stop set once, l > 0 ? l / p : l * p. p == 1 or last_n == 0
178 // is off. The caller validates the ranges (p in [1, 2], last_n in [0, ctx]).
180
181 // Timing of the most recent generate() call up to its first token.
182 const TtftBreakdown& last_ttft() const { return last_ttft_; }
183
184 // Chat mode: a GGUF whose chat template resolved at load (paperkiln.chat_template, paperkiln's
185 // specials at ids 0-3, or a third-party record) and whose vocabulary ember tokenises
186 // (chat_tokenizer.h). In this mode generate() takes the prompt verbatim (the caller renders
187 // the template; chat_request.h), encodes it with the GGUF's own tokenizer and no added BOS,
188 // stops at the record's stop ids (stop.eos "eos", stop.turn "turn"; the stop id is cut), and
189 // strips the reply's leading and trailing whitespace. Repetition penalty is off, matching
190 // mtstudio chat.
191 bool is_bpe() const { return bpe_ != nullptr; }
192 // The chat format of chat mode (template and stop ids; chat_tokenizer.h), or nullptr.
193 const ember::ChatFormat* chat_format() const { return bpe_ ? &bpe_->format() : nullptr; }
194 // The chat template the GGUF resolved to at load (chat_tokenizer.h): chat mode's, or
195 // paperkiln/legacy-word for a word-level vocabulary (the server's word-level path); nullptr
196 // when the model serves raw completion only. chat_template_why() says how it was resolved, or
197 // why there is none.
199 return bpe_ ? &bpe_->format().tmpl : word_template_ ? word_template_.get() : nullptr;
200 }
201 const std::string& chat_template_why() const { return chat_template_why_; }
202 // The IR document the interpreter runs, or "" when the tuned llama path serves the file: for a
203 // front end that runs the same graph on another device (the browser's WebGPU path, deploy/wasm/web).
204 const std::string& ir_document() const { return ir_doc_; }
205 // Chat mode's stop set (the record's stop.eos and stop.turn ids).
206 bool is_chat_stop(int id) const { return bpe_ && bpe_->format().is_stop(id); }
207 bool is_chat_eos(int id) const { return bpe_ && bpe_->format().is_eos(id); }
208 int bpe_token_count(const std::string& text) const {
209 return bpe_ ? static_cast<int>(bpe_prompt_ids(text).size()) : 0;
210 }
211 // The ids generate() runs for a chat-mode prompt: the encoding, with the per-turn state ids
212 // inserted when the model has them (state_tokens.h).
213 std::vector<int> bpe_prompt_ids(const std::string& text) const {
214 if (!bpe_) return {};
215 std::vector<int> ids = bpe_->encode(text);
216 if (state_k_ > 0) {
217 const ember::ChatFormat& f = bpe_->format();
218 ids = ember::insert_state_blocks(ids, state_vocab_, state_k_, f.eos_ids.at(0), f.turn_ids.at(0),
219 f.turn_ids.at(1));
220 }
221 return ids;
222 }
223 // Per-turn state tokens (paperkiln.state_tokens.k; state_tokens.h): k, 0 without.
224 int state_k() const { return state_k_; }
225 // How the last generate() ended ("eos", "turn" or "length") and how many
226 // tokens it produced, the stop marker excluded. Set in BPE mode.
227 const std::string& last_stop_reason() const { return last_stop_reason_; }
229 // The exact ids behind the last BPE reply: the prompt as encoded, the
230 // sampled tokens (stop marker excluded) and the stop marker's id (-1 at
231 // "length"). Re-encoding the decoded, whitespace-stripped reply need not
232 // give back the sampled ids.
233 const std::vector<int>& last_prompt_ids() const { return last_prompt_ids_; }
234 const std::vector<int>& last_generated_ids() const { return last_generated_ids_; }
235 int last_stop_id() const { return last_stop_id_; }
236 // Reseeds the sampler (a request's "seed").
237 void set_seed(unsigned seed) { rng_.seed(seed); }
238 // Called with each generated token id (stop markers excluded) as
239 // generate() produces it; empty to stop streaming.
240 void set_token_callback(std::function<void(int)> cb) { token_callback_ = std::move(cb); }
241 // Test hook (chat mode): each of the next generate() calls takes the next of these id sequences in
242 // place of the model's samples, after tokenising its prompt as usual: the ids go to the token
243 // callback in order up to max_new tokens or a stop id, and the reply, stop reason and ids are set
244 // from them exactly as from sampled ids; the KV cache is left out of it (the next real request
245 // prefills from position 0). The server sets it from EMBER_TEST_SCRIPTED_REPLIES (never under
246 // EMBER_PUBLIC=1), for tools/chat_v2_server_check.py's tool-call replies.
247 void set_scripted_replies(std::vector<std::vector<int>> r) { scripted_replies_ = std::move(r); }
248 // BPE detokenization of ids (BPE chat models only; empty otherwise).
249 std::string bpe_decode(const std::vector<int>& ids) const { return bpe_ ? bpe_->decode(ids) : std::string(); }
250 std::vector<int> bpe_encode(const std::string& text) const { return bpe_ ? bpe_->encode(text) : std::vector<int>(); }
251 // One sampling step outside generate(), for a caller that computes the logits itself (ember's
252 // WebGPU page): the contract repetition penalty (set_repeat_penalty) over `context` (prompt +
253 // reply so far, the stop set excluded), then the sampler and RNG generate() uses. Chat mode only.
254 int sample_step(std::vector<float>& logits, const std::vector<int>& context, float temperature, int top_k,
255 float top_p);
256
257 // POST /score (score.h, score.cpp; mtstudio score's semantics). score_rows: the logits of rows
258 // first_row .. ids.size() - 1 of one forward over `ids` from position 0, as a fresh prefill of `ids`
259 // gives them ([ids.size() - first_row][score_vocab()], row-major). With reuse_prefix, the forward
260 // keeps the leading positions (up to first_row) that `ids` shares with the previous score_rows
261 // call's ids when score_reuse_ok() (the result is then the same bits); the caller passes false
262 // for the first call of a batch and holds the session for the whole batch. *reused, if given,
263 // receives the positions kept. Afterwards generate() reuses nothing from the cache.
264 std::vector<float> score_rows(const std::vector<int>& ids, int first_row, bool reuse_prefix, int* reused = nullptr);
265 int score_vocab() const; // the logits' width
266 int score_context() const; // positions one forward holds (mtstudio's T)
267 // Prefix reuse gives the bits a fresh prefill gives: the CPU paths (every row computed apart
268 // from the batch it is in), not the device, recurrent state, eviction, depth routing or state
269 // tokens. EMBER_SCORE_REUSE=0 turns it off.
270 bool score_reuse_ok() const;
271 // The GGUF's tokenizer.ggml.tokens (empty without one): the argmax's banned specials by name.
272 const std::vector<std::string>& gguf_tokens() const;
273
274 private:
277
278 // Parallel batch processing methods
279 bool batch_prefill_parallel(const std::vector<std::vector<int>>& all_tokens,
280 const std::vector<int>& prompt_lengths,
281 std::vector<std::vector<float>>& batch_final_logits);
282
283 bool batch_generation_parallel(const std::vector<int>& current_tokens,
284 const std::vector<int>& token_positions,
285 const std::vector<int>& original_sequence_indices,
286 std::vector<std::vector<float>>& batch_logits);
287
288 std::unique_ptr<TinyLlamaModel> model_;
289 // paperkiln IR interpreter (graph_ir.h); when set, prefill and decode run
290 // through it on the CPU instead of the llama path.
291 std::unique_ptr<ember_ir::Interpreter> ir_;
292 std::string ir_doc_; // the document ir_ runs (ir_document())
293 std::unique_ptr<Tokenizer> tokenizer_;
297 std::mt19937 rng_{std::random_device{}()}; // RNG for sampling
298 float repetition_penalty_ = 1.3f; // see set_repetition_penalty
299 float repeat_penalty_ = 1.0f; // see set_repeat_penalty
302 bool use_batch_generation_; // Enable batch generation
303 std::stringstream generated_stream_; // Added for streaming output
304 std::string generated_text_for_api_return_; // Added to accumulate full response
305 TtftBreakdown last_ttft_; // see last_ttft()
306 std::vector<int> kv_token_history_; // token ids the previous generate() left in the KV cache
307 std::unique_ptr<ember::ChatTokenizer> bpe_; // set in chat mode; see is_bpe()
308 std::unique_ptr<microtorch::chat::Template> word_template_; // see chat_template()
310 std::string last_stop_reason_;
314 std::function<void(int)> token_callback_;
315 std::vector<std::vector<int>> scripted_replies_; // see set_scripted_replies()
316 int state_k_ = 0, state_vocab_ = 0; // see state_k(); the state ids are state_vocab_ + j
317 std::vector<int> score_ids_; // the ids the last score_rows() left in the cache
318
319 // Batch processing support
320 int max_batch_size_ = 1; // Maximum number of sequences for batch processing
321};
322
323} // namespace tinyllama
324
325#endif
A lightweight tokenizer implementation for text processing.
Definition tokenizer.h:61
Represents an active TinyLlama session holding the loaded model and tokenizer.
Definition api.h:85
ModelConfig config_
Definition api.h:294
int last_generated_tokens() const
Definition api.h:228
bool batch_generation_parallel(const std::vector< int > &current_tokens, const std::vector< int > &token_positions, const std::vector< int > &original_sequence_indices, std::vector< std::vector< float > > &batch_logits)
Definition api.cpp:2063
std::vector< float > score_rows(const std::vector< int > &ids, int first_row, bool reuse_prefix, int *reused=nullptr)
Definition score.cpp:56
std::string chat_template_why_
Definition api.h:309
int score_context() const
Definition score.cpp:32
bool score_reuse_ok() const
Definition score.cpp:44
int state_k() const
Definition api.h:224
std::unique_ptr< ember::ChatTokenizer > bpe_
Definition api.h:307
bool batch_prefill_parallel(const std::vector< std::vector< int > > &all_tokens, const std::vector< int > &prompt_lengths, std::vector< std::vector< float > > &batch_final_logits)
Definition api.cpp:1784
bool is_bpe() const
Definition api.h:191
std::string bpe_decode(const std::vector< int > &ids) const
Definition api.h:249
std::mt19937 rng_
Definition api.h:297
std::stringstream generated_stream_
Definition api.h:303
float get_repetition_penalty() const
Definition api.h:172
std::vector< int > last_prompt_ids_
Definition api.h:312
std::string last_stop_reason_
Definition api.h:310
std::unique_ptr< TinyLlamaModel > model_
Definition api.h:288
const std::vector< int > & last_generated_ids() const
Definition api.h:234
std::vector< std::vector< int > > scripted_replies_
Definition api.h:315
int last_stop_id() const
Definition api.h:235
const std::string & chat_template_why() const
Definition api.h:201
TinyLlamaSession(const TinyLlamaSession &)=delete
TtftBreakdown last_ttft_
Definition api.h:305
std::function< void(int)> token_callback_
Definition api.h:314
std::vector< int > bpe_prompt_ids(const std::string &text) const
Definition api.h:213
std::unique_ptr< ember_ir::Interpreter > ir_
Definition api.h:291
std::unique_ptr< Tokenizer > tokenizer_
Definition api.h:293
std::vector< int > score_ids_
Definition api.h:317
const Tokenizer * get_tokenizer() const
Definition api.h:164
std::vector< int > kv_token_history_
Definition api.h:306
const TtftBreakdown & last_ttft() const
Definition api.h:182
const ember::ChatFormat * chat_format() const
Definition api.h:193
std::string generate(const std::string &prompt, int steps=128, float temperature=0.1f, int top_k=40, float top_p=0.9f, const std::string &system_prompt="", bool apply_q_a_format=false)
Generates text based on a given prompt.
Definition api.cpp:706
std::string ir_doc_
Definition api.h:292
const std::string & last_stop_reason() const
Definition api.h:227
void set_repeat_penalty(float p, int last_n)
Definition api.h:179
KVCache & get_kv_cache()
Definition api.h:166
void set_repetition_penalty(float p)
Definition api.h:171
const std::vector< int > & last_prompt_ids() const
Definition api.h:233
std::vector< std::string > generate_batch(const std::vector< std::string > &prompts, int steps=128, float temperature=0.1f, int top_k=40, float top_p=0.9f, const std::string &system_prompt="", bool apply_q_a_format=false)
Generates text for multiple prompts in a single batch (parallel processing).
Definition api.cpp:1473
const ModelConfig & get_config() const
Definition api.h:165
std::vector< int > last_generated_ids_
Definition api.h:312
bool is_chat_stop(int id) const
Definition api.h:206
std::string generated_text_for_api_return_
Definition api.h:304
void set_token_callback(std::function< void(int)> cb)
Definition api.h:240
~TinyLlamaSession()
Destructor to ensure proper cleanup (e.g., KVCache CUDA memory).
Definition api.cpp:702
std::vector< int > bpe_encode(const std::string &text) const
Definition api.h:250
void set_scripted_replies(std::vector< std::vector< int > > r)
Definition api.h:247
void set_seed(unsigned seed)
Definition api.h:237
std::unique_ptr< microtorch::chat::Template > word_template_
Definition api.h:308
bool is_chat_eos(int id) const
Definition api.h:207
const std::string & ir_document() const
Definition api.h:204
int bpe_token_count(const std::string &text) const
Definition api.h:208
const microtorch::chat::Template * chat_template() const
Definition api.h:198
const std::vector< std::string > & gguf_tokens() const
Definition score.cpp:38
TinyLlamaSession & operator=(const TinyLlamaSession &)=delete
int sample_step(std::vector< float > &logits, const std::vector< int > &context, float temperature, int top_k, float top_p)
Definition api.cpp:1464
const float * p[kIrDepthSourcesMax]
std::string text
size_t j
Definition api.h:20
std::vector< int > insert_state_blocks(const std::vector< int > &ids, int vocab, int k, int eot, int user, int assistant)
Complete Key-Value cache for all transformer layers.
Definition model.h:157
Model configuration structure holding architecture and hyperparameters.
Definition model.h:82
Where the time went between generate() being called and its first output token being emitted (docs/TT...
Definition api.h:37
std::chrono::steady_clock::time_point t_first_token
Definition api.h:39
double prefix_hit_rate
Definition api.h:59
int prefix_prev_cached_tokens
Definition api.h:57
std::map< std::string, double > op_ms
Definition api.h:74
std::chrono::steady_clock::time_point t_entry
Definition api.h:38
double prefill_gpu_span_ms
Definition api.h:48
double prefill_wall_ms
Definition api.h:46