Skip to content

Commit 46ee566

Browse files
committed
aws bedrock embedding
1 parent 4b263e9 commit 46ee566

File tree

9 files changed

+94
-98
lines changed

9 files changed

+94
-98
lines changed

lib/instrumentation/aws-sdk/v3/bedrock.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
const {
88
LlmChatCompletionMessage,
99
LlmChatCompletionSummary,
10-
LlmEmbedding,
1110
LlmErrorMessage,
1211
BedrockCommand,
1312
BedrockResponse,
1413
StreamHandler
1514
} = require('../../../llm-events/aws-bedrock')
15+
const {
16+
LlmEmbedding
17+
} = require('../../../llm-events-new/aws-bedrock')
1618

1719
const { DESTINATIONS } = require('../../../config/attribute-filter')
1820
const { AI } = require('../../../metrics/names')
@@ -73,7 +75,7 @@ function isStreamingEnabled({ commandName, config }) {
7375
* @param {object} params.msg LLM event
7476
*/
7577
function recordEvent({ agent, type, msg }) {
76-
msg.serialize()
78+
if (msg.serialize) msg.serialize() // TODO: remove
7779
const llmContext = extractLlmContext(agent)
7880
const timestamp = msg?.timestamp ?? Date.now()
7981

@@ -216,10 +218,11 @@ function recordEmbeddingMessage({
216218
agent,
217219
segment,
218220
transaction,
219-
bedrockCommand,
220-
input: prompt.content,
221-
bedrockResponse,
222-
isError: err !== null
221+
requestModel: bedrockCommand?.modelId,
222+
requestInput: prompt.content,
223+
requestId: bedrockResponse?.requestId,
224+
totalTokenCount: bedrockResponse?.totalTokenCount,
225+
error: err !== null
223226
}))
224227

225228
for (const embedding of embeddings) {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
const LlmEmbedding = require('../embedding')
7+
8+
/**
9+
* Encapsulates a AWS Bedrock LlmEmbedding event.
10+
*/
11+
module.exports = class AwsBedrockLlmEmbedding extends LlmEmbedding {
12+
/**
13+
*
14+
* @param {object} params constructor parameters
15+
* @param {Agent} params.agent New Relic agent instance
16+
* @param {object} params.segment Current segment
17+
* @param {object} params.transaction Current and active transaction
18+
* @param {string} params.requestId ID associated with the request - typically available in response headers
19+
* @param {string} params.requestInput Input to the embedding creation call
20+
* @param {string} params.requestModel Model name specified in the request (e.g. 'gpt-4')
21+
* @param {number} params.totalTokenCount Retrieved from the Bedrock response object, fallback for token calculation
22+
* @param {boolean} [params.error] Set to `true` if an error occurred during creation call - omitted if no error occurred
23+
*/
24+
constructor({ agent, segment, transaction, requestInput, requestModel, requestId, totalTokenCount = 0, error }) {
25+
super({ agent,
26+
segment,
27+
transaction,
28+
vendor: 'bedrock',
29+
requestInput,
30+
requestModel,
31+
requestId,
32+
error })
33+
34+
this.setTotalTokens(agent, requestInput, totalTokenCount)
35+
}
36+
37+
setTotalTokens(agent, input, totalTokenCount) {
38+
const tokenCB = agent?.llm?.tokenCountCallback
39+
40+
// For embedding events, only total token count is relevant.
41+
// Prefer callback for total tokens; if unavailable, fall back to response data.
42+
if (tokenCB) {
43+
const content = input?.toString()
44+
45+
if (content === undefined) {
46+
return
47+
}
48+
49+
const totalTokens = this.calculateCallbackTokens(tokenCB, this['request.model'], content)
50+
this.setTokensOnEmbeddingMessage(totalTokens)
51+
return
52+
}
53+
54+
this.setTokensOnEmbeddingMessage(totalTokenCount)
55+
}
56+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
8+
const LlmEmbedding = require('./embedding')
9+
10+
module.exports = {
11+
LlmEmbedding
12+
}

lib/llm-events-new/base.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ class LlmEvent {
8686
return Number(this['response.usage.prompt_tokens']) + Number(this['response.usage.completion_tokens'])
8787
}
8888

89+
/**
90+
* If `totalTokens` is valid, assigns it to
91+
* `this['response.usage.total_tokens']`.
92+
* @param {number} totalTokens total tokens on embedding message
93+
*/
8994
setTokensOnEmbeddingMessage(totalTokens) {
9095
if (this.validTokenCount(totalTokens)) {
9196
this['response.usage.total_tokens'] = totalTokens

lib/llm-events-new/embedding.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ class LlmEmbedding extends LlmEvent {
2323
* @param {Agent} params.agent New Relic agent instance
2424
* @param {object} params.segment Current segment
2525
* @param {object} params.transaction Current and active transaction
26-
* @param {string} params.requestId ID associated with the request - typically available in response headers
26+
* @param {string} [params.requestId] ID associated with the request - typically available in response headers
2727
* @param {string} params.requestInput Input to the embedding creation call
28-
* @param {string} params.requestModel Model name specified in the request (e.g. 'gpt-4')
29-
* @param {string} params.responseModel Model name returned in the response (can differ from `request.model`)
30-
* @param {string} params.responseOrg Organization ID returned in the response or response headers
28+
* @param {string} [params.requestModel] Model name specified in the request (e.g. 'gpt-4')
29+
* @param {string} [params.responseModel] Model name returned in the response (can differ from `request.model`)
30+
* @param {string} [params.responseOrg] Organization ID returned in the response or response headers
3131
* @param {string} params.vendor Lowercased name of vendor (e.g. 'openai')
3232
* @param {boolean} [params.error] Set to `true` if an error occurred during creation call - omitted if no error occurred
3333
*/

lib/llm-events/aws-bedrock/embedding.js

Lines changed: 0 additions & 55 deletions
This file was deleted.

lib/llm-events/aws-bedrock/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ module.exports = {
1010
BedrockResponse: require('./bedrock-response'),
1111
LlmChatCompletionMessage: require('./chat-completion-message'),
1212
LlmChatCompletionSummary: require('./chat-completion-summary'),
13-
LlmEmbedding: require('./embedding'),
1413
LlmEvent: require('./event'),
1514
LlmErrorMessage: require('../error-message'),
1615
StreamHandler: require('./stream-handler'),

test/unit/llm-events/aws-bedrock/embedding.test.js

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const assert = require('node:assert')
1010
const {
1111
DESTINATIONS: { TRANS_SCOPE }
1212
} = require('../../../../lib/config/attribute-filter')
13-
const LlmEmbedding = require('../../../../lib/llm-events/aws-bedrock/embedding')
13+
const LlmEmbedding = require('#agentlib/llm-events-new/aws-bedrock/embedding.js')
1414

1515
test.beforeEach((ctx) => {
1616
ctx.nr = {}
@@ -45,19 +45,10 @@ test.beforeEach((ctx) => {
4545
}
4646
}
4747

48-
ctx.nr.bedrockCommand = {
49-
}
50-
51-
ctx.nr.input = 'who are you'
48+
ctx.nr.requestInput = 'who are you'
49+
ctx.nr.requestId = 'request-1'
50+
ctx.nr.totalTokenCount = 70
5251

53-
ctx.nr.bedrockResponse = {
54-
headers: {
55-
'x-amzn-requestid': 'request-1'
56-
},
57-
get inputTokenCount() {
58-
return 70
59-
}
60-
}
6152
ctx.nr.transaction = {
6253
traceId: 'id'
6354
}
@@ -82,17 +73,13 @@ test('should not capture input when `ai_monitoring.record_content.enabled` is fa
8273
assert.equal(event.input, undefined, 'input should be empty')
8374
})
8475

85-
test('capture total token usage attribute when inputTokenCount is set', async (t) => {
76+
test('capture total token usage attribute when totalTokenCount is set', async (t) => {
8677
const event = new LlmEmbedding(t.nr)
8778
assert.equal(event['response.usage.total_tokens'], 70)
8879
})
8980

90-
test('does not capture total token usage when inputTokenCount is not set', async (t) => {
91-
Object.defineProperty(t.nr.bedrockResponse, 'inputTokenCount', {
92-
get() {
93-
return undefined
94-
}
95-
})
81+
test('does not capture total token usage when totalTokenCount is not set', async (t) => {
82+
t.nr.totalTokenCount = undefined
9683
const event = new LlmEmbedding(t.nr)
9784
assert.equal(event['response.usage.total_tokens'], undefined)
9885
})
@@ -106,14 +93,3 @@ test('should use token callback to set total token usage attribute', async (t) =
10693

10794
assert.equal(event['response.usage.total_tokens'], 65)
10895
})
109-
110-
test('should not call token callback if there is no content', async (t) => {
111-
function cb(model, content) {
112-
return 65
113-
}
114-
t.nr.agent.llm.tokenCountCallback = cb
115-
t.nr.input = undefined
116-
const event = new LlmEmbedding(t.nr)
117-
118-
assert.equal(event['response.usage.total_tokens'], undefined)
119-
})

test/versioned/aws-sdk-v3/bedrock-chat-completions.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ test('cohere embedding streaming works', async (t) => {
459459
const events = agent.customEventAggregator.events.toArray()
460460
assert.equal(events.length, 1)
461461
const embedding = events.shift()[1]
462-
assert.equal(embedding.error, false)
462+
assert.equal(embedding.error, undefined)
463463
assert.equal(embedding.input, prompt)
464464

465465
tx.end()

0 commit comments

Comments
 (0)