-
Notifications
You must be signed in to change notification settings - Fork 0
DUX-7485: Capture streaming usage tokens and context management for Bedrock InvokeModel #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
16c1731
7bdc118
6213d75
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,7 +21,7 @@ def stream_url | |
| end | ||
|
|
||
| def stream_response(payload, additional_headers = {}, &block) | ||
| accumulator = StreamAccumulator.new | ||
| accumulator = StreamAccumulator.new(net_cache_tokens: true) | ||
| decoder = event_stream_decoder | ||
| thinking_state = {} | ||
| body = JSON.generate(payload) | ||
|
|
@@ -48,9 +48,19 @@ def stream_response(payload, additional_headers = {}, &block) | |
|
|
||
| message = accumulator.to_message(response) | ||
| RubyLLM.logger.debug { "Stream completed: #{message.content}" } | ||
| log_context_management(message) | ||
| message | ||
| end | ||
|
|
||
| def log_context_management(message) | ||
| applied_edits = message.context_management && message.context_management['applied_edits'] | ||
| return unless applied_edits && !applied_edits.empty? | ||
|
|
||
| RubyLLM.logger.debug do | ||
| "Bedrock InvokeModel context_management applied_edits: #{applied_edits.inspect}" | ||
| end | ||
| end | ||
|
|
||
| def event_stream_decoder | ||
| require 'aws-eventstream' | ||
| Aws::EventStream::Decoder.new | ||
|
|
@@ -161,13 +171,20 @@ def build_chunk(event, thinking_state = {}) | |
| def build_message_start_chunk(event) | ||
| message = event['message'] || {} | ||
| usage = message['usage'] || {} | ||
| cache_read = usage['cache_read_input_tokens'] | ||
| cache_creation = usage['cache_creation_input_tokens'] | ||
| input_tok = usage['input_tokens'] | ||
| cache_creation_detail = usage['cache_creation'] || {} | ||
|
|
||
| Chunk.new( | ||
| role: :assistant, | ||
| content: nil, | ||
| model_id: message['model'] || @model&.id, | ||
| input_tokens: input_tok ? [input_tok.to_i, 0].max : nil | ||
| input_tokens: input_tok&.to_i, | ||
| cached_tokens: cache_read, | ||
| cache_creation_tokens: cache_creation, | ||
| cache_creation_ephemeral_5m_tokens: cache_creation_detail['ephemeral_5m_input_tokens'], | ||
| cache_creation_ephemeral_1h_tokens: cache_creation_detail['ephemeral_1h_input_tokens'] | ||
| ) | ||
| end | ||
|
|
||
|
|
@@ -254,13 +271,20 @@ def build_content_block_stop_chunk(event, thinking_state) | |
| def build_message_delta_chunk(event) | ||
| delta = event['delta'] || {} | ||
| usage = event['usage'] || {} | ||
| cache_creation_detail = usage['cache_creation'] || {} | ||
|
|
||
| Chunk.new( | ||
| role: :assistant, | ||
| content: nil, | ||
| model_id: @model&.id, | ||
| output_tokens: usage['output_tokens'], | ||
| finish_reason: delta['stop_reason'] | ||
| cached_tokens: usage['cache_read_input_tokens'], | ||
| cache_creation_tokens: usage['cache_creation_input_tokens'], | ||
| cache_creation_ephemeral_5m_tokens: cache_creation_detail['ephemeral_5m_input_tokens'], | ||
| cache_creation_ephemeral_1h_tokens: cache_creation_detail['ephemeral_1h_input_tokens'], | ||
|
Comment on lines
+281
to
+284
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These lines extract |
||
| finish_reason: delta['stop_reason'], | ||
| stop_sequence: delta['stop_sequence'], | ||
| context_management: delta['context_management'] | ||
| ) | ||
| end | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
cached_tokens:andcache_creation_tokens:are passed as raw JSON values here, butinput_tokens:gets an explicit.to_icall. Since JSON parsing already yields integers, this isn't a crash risk, but the inconsistency is worth noting — if the pattern ever changes or a non-numeric value slips through,netted_input_tokens's.to_ifallback instream_accumulator.rbmasks the mismatch rather than raising early.Consider applying
.to_ito all three token values for consistency with theinput_tok&.to_ipattern directly above.