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
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.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/token_reel/timeline.rb', line 27 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 @prompt_done_t = @prompt_interval * prompt_tokens.size @thinking_end_t = prompt_done_t + config.ttft.to_f @reasoning_end_t = thinking_end_t + @reasoning_interval * reasoning_tokens.size @stream_end_t = reasoning_end_t + @response_interval * response_tokens.size @duration = stream_end_t + config.hold.to_f end |
Instance Attribute Details
#duration ⇒ Object (readonly)
Returns the value of attribute duration.
23 24 25 |
# File 'lib/token_reel/timeline.rb', line 23 def duration @duration end |
#prompt_done_t ⇒ Object (readonly)
Returns the value of attribute prompt_done_t.
23 24 25 |
# File 'lib/token_reel/timeline.rb', line 23 def prompt_done_t @prompt_done_t end |
#prompt_full ⇒ Object (readonly)
Returns the value of attribute prompt_full.
23 24 25 |
# File 'lib/token_reel/timeline.rb', line 23 def prompt_full @prompt_full end |
#prompt_tokens ⇒ Object (readonly)
Returns the value of attribute prompt_tokens.
23 24 25 |
# File 'lib/token_reel/timeline.rb', line 23 def prompt_tokens @prompt_tokens end |
#reasoning_end_t ⇒ Object (readonly)
Returns the value of attribute reasoning_end_t.
23 24 25 |
# File 'lib/token_reel/timeline.rb', line 23 def reasoning_end_t @reasoning_end_t end |
#reasoning_full ⇒ Object (readonly)
Returns the value of attribute reasoning_full.
23 24 25 |
# File 'lib/token_reel/timeline.rb', line 23 def reasoning_full @reasoning_full end |
#reasoning_tokens ⇒ Object (readonly)
Returns the value of attribute reasoning_tokens.
23 24 25 |
# File 'lib/token_reel/timeline.rb', line 23 def reasoning_tokens @reasoning_tokens end |
#response_full ⇒ Object (readonly)
Returns the value of attribute response_full.
23 24 25 |
# File 'lib/token_reel/timeline.rb', line 23 def response_full @response_full end |
#response_tokens ⇒ Object (readonly)
Returns the value of attribute response_tokens.
23 24 25 |
# File 'lib/token_reel/timeline.rb', line 23 def response_tokens @response_tokens end |
#stream_end_t ⇒ Object (readonly)
Returns the value of attribute stream_end_t.
23 24 25 |
# File 'lib/token_reel/timeline.rb', line 23 def stream_end_t @stream_end_t end |
#thinking_end_t ⇒ Object (readonly)
Returns the value of attribute thinking_end_t.
23 24 25 |
# File 'lib/token_reel/timeline.rb', line 23 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.
100 101 102 103 |
# File 'lib/token_reel/timeline.rb', line 100 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.
109 110 111 112 |
# File 'lib/token_reel/timeline.rb', line 109 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
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 |
# File 'lib/token_reel/timeline.rb', line 47 def state_at(t) t = t.clamp(0, duration) if t < prompt_done_t n = @prompt_interval.positive? ? (t / @prompt_interval).floor : 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? ? (elapsed / @reasoning_interval).floor : 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? ? (elapsed / @response_interval).floor : 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 |