Class: TokenReel::Timeline
- Inherits:
-
Object
- Object
- TokenReel::Timeline
- Defined in:
- lib/token_reel/timeline.rb
Overview
Pure function of time -> State. Doesn't touch the filesystem or ImageMagick at all, which makes it cheap to sample as densely as we like when building the frame list.
Constant Summary collapse
- BLINK_HZ =
cursor toggles this many times per second
2.0- DOT_INTERVAL =
seconds between "thinking..." dot ticks
0.35- JITTER_PCTS =
When config.variability is on, each token's delay is independently jittered by a normally-distributed offset, bounded to +/- one of these percentages (picked at random per token) of its base interval.
[0.10, 0.20, 0.30].freeze
Instance Attribute Summary collapse
-
#duration ⇒ Object
readonly
Returns the value of attribute duration.
-
#prompt_done_t ⇒ Object
readonly
Returns the value of attribute prompt_done_t.
-
#prompt_full ⇒ Object
readonly
Returns the value of attribute prompt_full.
-
#prompt_tokens ⇒ Object
readonly
Returns the value of attribute prompt_tokens.
-
#reasoning_end_t ⇒ Object
readonly
Returns the value of attribute reasoning_end_t.
-
#reasoning_full ⇒ Object
readonly
Returns the value of attribute reasoning_full.
-
#reasoning_tokens ⇒ Object
readonly
Returns the value of attribute reasoning_tokens.
-
#response_full ⇒ Object
readonly
Returns the value of attribute response_full.
-
#response_tokens ⇒ Object
readonly
Returns the value of attribute response_tokens.
-
#stream_end_t ⇒ Object
readonly
Returns the value of attribute stream_end_t.
-
#thinking_end_t ⇒ Object
readonly
Returns the value of attribute thinking_end_t.
Instance Method Summary collapse
-
#final_state ⇒ Object
The fully-revealed, cursor-off state -- used to size the canvas so every frame in the GIF shares identical dimensions.
-
#initialize(config) ⇒ Timeline
constructor
A new instance of Timeline.
-
#max_reasoning_state ⇒ Object
The reasoning phase in full, cursor off -- used alongside final_state to size the canvas, since the reasoning trace (shown only while streaming, then replaced by the response) can be taller than the finished response.
- #state_at(t) ⇒ Object
Constructor Details
#initialize(config) ⇒ Timeline
Returns a new instance of Timeline.
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 |
# File 'lib/token_reel/timeline.rb', line 32 def initialize(config) @config = config @prompt_tokens = Tokenizer.tokenize(config.prompt, config.unit) @reasoning_tokens = Tokenizer.tokenize(config.reasoning, config.unit) @response_tokens = Tokenizer.tokenize(config.response, config.unit) @prompt_full = prompt_tokens.join @reasoning_full = reasoning_tokens.join @response_full = response_tokens.join @prompt_interval = config.prompt_tps.to_f.positive? ? 1.0 / config.prompt_tps : 0 @reasoning_interval = config.reasoning_tps.to_f.positive? ? 1.0 / config.reasoning_tps : 1.0 / config.tps @response_interval = 1.0 / config.tps @rng = config.variability ? Random.new(config.seed || Random.new_seed) : nil # Cumulative reveal times per token: offsets[n] is the moment the # n-th token has fully appeared. With variability off this is just # n * interval; with it on, each step is independently jittered, # so the array can't be derived by a plain multiply anymore. @prompt_offsets = cumulative_offsets(prompt_tokens.size, @prompt_interval) @reasoning_offsets = cumulative_offsets(reasoning_tokens.size, @reasoning_interval) @response_offsets = cumulative_offsets(response_tokens.size, @response_interval) @prompt_done_t = @prompt_offsets.last @thinking_end_t = prompt_done_t + config.ttft.to_f @reasoning_end_t = thinking_end_t + @reasoning_offsets.last @stream_end_t = reasoning_end_t + @response_offsets.last @duration = stream_end_t + config.hold.to_f end |
Instance Attribute Details
#duration ⇒ Object (readonly)
Returns the value of attribute duration.
28 29 30 |
# File 'lib/token_reel/timeline.rb', line 28 def duration @duration end |
#prompt_done_t ⇒ Object (readonly)
Returns the value of attribute prompt_done_t.
28 29 30 |
# File 'lib/token_reel/timeline.rb', line 28 def prompt_done_t @prompt_done_t end |
#prompt_full ⇒ Object (readonly)
Returns the value of attribute prompt_full.
28 29 30 |
# File 'lib/token_reel/timeline.rb', line 28 def prompt_full @prompt_full end |
#prompt_tokens ⇒ Object (readonly)
Returns the value of attribute prompt_tokens.
28 29 30 |
# File 'lib/token_reel/timeline.rb', line 28 def prompt_tokens @prompt_tokens end |
#reasoning_end_t ⇒ Object (readonly)
Returns the value of attribute reasoning_end_t.
28 29 30 |
# File 'lib/token_reel/timeline.rb', line 28 def reasoning_end_t @reasoning_end_t end |
#reasoning_full ⇒ Object (readonly)
Returns the value of attribute reasoning_full.
28 29 30 |
# File 'lib/token_reel/timeline.rb', line 28 def reasoning_full @reasoning_full end |
#reasoning_tokens ⇒ Object (readonly)
Returns the value of attribute reasoning_tokens.
28 29 30 |
# File 'lib/token_reel/timeline.rb', line 28 def reasoning_tokens @reasoning_tokens end |
#response_full ⇒ Object (readonly)
Returns the value of attribute response_full.
28 29 30 |
# File 'lib/token_reel/timeline.rb', line 28 def response_full @response_full end |
#response_tokens ⇒ Object (readonly)
Returns the value of attribute response_tokens.
28 29 30 |
# File 'lib/token_reel/timeline.rb', line 28 def response_tokens @response_tokens end |
#stream_end_t ⇒ Object (readonly)
Returns the value of attribute stream_end_t.
28 29 30 |
# File 'lib/token_reel/timeline.rb', line 28 def stream_end_t @stream_end_t end |
#thinking_end_t ⇒ Object (readonly)
Returns the value of attribute thinking_end_t.
28 29 30 |
# File 'lib/token_reel/timeline.rb', line 28 def thinking_end_t @thinking_end_t end |
Instance Method Details
#final_state ⇒ Object
The fully-revealed, cursor-off state -- used to size the canvas so every frame in the GIF shares identical dimensions.
115 116 117 118 |
# File 'lib/token_reel/timeline.rb', line 115 def final_state State.new(phase: :done, prompt_text: prompt_full, reasoning_text: "", response_text: response_full, cursor_on: false, dot_count: 0) end |
#max_reasoning_state ⇒ Object
The reasoning phase in full, cursor off -- used alongside final_state to size the canvas, since the reasoning trace (shown only while streaming, then replaced by the response) can be taller than the finished response.
124 125 126 127 |
# File 'lib/token_reel/timeline.rb', line 124 def max_reasoning_state State.new(phase: :reasoning, prompt_text: prompt_full, reasoning_text: reasoning_full, response_text: "", cursor_on: false, dot_count: 0) end |
#state_at(t) ⇒ Object
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 |
# File 'lib/token_reel/timeline.rb', line 62 def state_at(t) t = t.clamp(0, duration) if t < prompt_done_t n = @prompt_interval.positive? ? tokens_shown(@prompt_offsets, t) : prompt_tokens.size State.new( phase: :typing_prompt, prompt_text: prompt_tokens[0...n].join, reasoning_text: "", response_text: "", cursor_on: blink_on?(t), dot_count: 0 ) elsif t < thinking_end_t elapsed = t - prompt_done_t State.new( phase: :thinking, prompt_text: prompt_full, reasoning_text: "", response_text: "", cursor_on: blink_on?(t), dot_count: ((elapsed / DOT_INTERVAL).to_i % 4) ) elsif t < reasoning_end_t elapsed = t - thinking_end_t n = @reasoning_interval.positive? ? tokens_shown(@reasoning_offsets, elapsed) : reasoning_tokens.size n = n.clamp(0, reasoning_tokens.size) State.new( phase: :reasoning, prompt_text: prompt_full, reasoning_text: reasoning_tokens[0...n].join, response_text: "", cursor_on: blink_on?(t), dot_count: 0 ) else elapsed = t - reasoning_end_t n = @response_interval.positive? ? tokens_shown(@response_offsets, elapsed) : response_tokens.size n = n.clamp(0, response_tokens.size) done = n >= response_tokens.size State.new( phase: done ? :done : :streaming, prompt_text: prompt_full, reasoning_text: "", response_text: response_tokens[0...n].join, cursor_on: blink_on?(t), dot_count: 0 ) end end |