-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Expose usage counts in OpenAI streamed responses (Fixes #2003) #2016
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
6b46184
e4482b6
331d25d
044c702
eb900be
44d31c2
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 | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -262,6 +262,50 @@ class ClaudeService extends BaseService { | |||||||||
| const init_chat_stream = async ({ chatStream }) => { | ||||||||||
| const completion = await anthropic.messages.stream(sdk_params); | ||||||||||
| const usageSum = {}; | ||||||||||
| const runningUsage = { | ||||||||||
| input_tokens: 0, | ||||||||||
| output_tokens: 0, | ||||||||||
| total_tokens: 0, | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| // Each emitted content block now carries an incremental usage object | ||||||||||
| // ({ input_tokens, output_tokens, total_tokens }) for live metering. | ||||||||||
| const getUsage = () => ({ | ||||||||||
|
Collaborator
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. can just spread op the data to copy it {...runningUsage} |
||||||||||
| input_tokens: runningUsage.input_tokens, | ||||||||||
| output_tokens: runningUsage.output_tokens, | ||||||||||
| total_tokens: runningUsage.total_tokens, | ||||||||||
| }); | ||||||||||
|
|
||||||||||
| const enhanceTextBlock = (block) => { | ||||||||||
| block.addText = (text) => { | ||||||||||
| const payload = { | ||||||||||
| type: 'text', | ||||||||||
| text, | ||||||||||
| usage: getUsage(), | ||||||||||
|
Collaborator
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.
Suggested change
|
||||||||||
| }; | ||||||||||
| block.chatStream.stream.write(JSON.stringify(payload) + '\n'); | ||||||||||
| }; | ||||||||||
| return block; | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| const enhanceToolBlock = (block) => { | ||||||||||
| const originalAddPartialJSON = block.addPartialJSON?.bind(block); | ||||||||||
| if ( originalAddPartialJSON ) { | ||||||||||
| block.addPartialJSON = (partial_json) => originalAddPartialJSON(partial_json); | ||||||||||
| } | ||||||||||
| block.end = () => { | ||||||||||
| const buffer = (block.buffer || '').trim() === '' ? '{}' : block.buffer; | ||||||||||
| const payload = { | ||||||||||
| ...block.contentBlock, | ||||||||||
| input: JSON.parse(buffer), | ||||||||||
| ...(block.contentBlock?.text ? {} : { text: '' }), | ||||||||||
| type: 'tool_use', | ||||||||||
|
Collaborator
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. This needs to go at top of the block, as stream block.contentBlock might override it, to match existing method |
||||||||||
| usage: getUsage(), | ||||||||||
|
Collaborator
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.
Suggested change
|
||||||||||
| }; | ||||||||||
| block.chatStream.stream.write(JSON.stringify(payload) + '\n'); | ||||||||||
| }; | ||||||||||
| return block; | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| let message, contentBlock; | ||||||||||
| for await ( const event of completion ) { | ||||||||||
|
|
@@ -272,6 +316,9 @@ class ClaudeService extends BaseService { | |||||||||
| if ( ! usageSum[key] ) usageSum[key] = 0; | ||||||||||
| usageSum[key] += meteredData[key]; | ||||||||||
| }); | ||||||||||
| runningUsage.input_tokens += meteredData.input_tokens || 0; | ||||||||||
|
Collaborator
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.
Suggested change
|
||||||||||
| runningUsage.output_tokens += meteredData.output_tokens || 0; | ||||||||||
| runningUsage.total_tokens = runningUsage.input_tokens + runningUsage.output_tokens; | ||||||||||
|
|
||||||||||
| if ( event.type === 'message_start' ) { | ||||||||||
| message = chatStream.message(); | ||||||||||
|
|
@@ -285,16 +332,16 @@ class ClaudeService extends BaseService { | |||||||||
|
|
||||||||||
| if ( event.type === 'content_block_start' ) { | ||||||||||
| if ( event.content_block.type === 'tool_use' ) { | ||||||||||
| contentBlock = message.contentBlock({ | ||||||||||
| contentBlock = enhanceToolBlock(message.contentBlock({ | ||||||||||
| type: event.content_block.type, | ||||||||||
| id: event.content_block.id, | ||||||||||
| name: event.content_block.name, | ||||||||||
| }); | ||||||||||
| })); | ||||||||||
| continue; | ||||||||||
| } | ||||||||||
| contentBlock = message.contentBlock({ | ||||||||||
| contentBlock = enhanceTextBlock(message.contentBlock({ | ||||||||||
| type: event.content_block.type, | ||||||||||
| }); | ||||||||||
| })); | ||||||||||
| continue; | ||||||||||
| } | ||||||||||
|
|
||||||||||
|
|
||||||||||
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.
should match actual claude usages to make it more visible