-
Notifications
You must be signed in to change notification settings - Fork 582
feat(instrumentation-aws-sdk): add bedrock extension to apply gen ai conventions #2700
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
Merged
Merged
Changes from 6 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
6cb00fa
feat(instrumentation-aws-sdk): add bedrock extension
anuraaga 765c280
Use minimum bedrock that supports Converse
anuraaga 94d7fe4
Cleanups
anuraaga 36ca8fd
Update deps
anuraaga fc8fd69
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
anuraaga 5bf7306
git add
anuraaga e2366aa
Cleanup
anuraaga 018cb5c
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
anuraaga b8e3c16
Extract converse handler
anuraaga c33aaaa
Add pre-implementation test case of InvokeModel
anuraaga f768a7d
git add
anuraaga 114211d
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
anuraaga 52900ed
TAV
anuraaga ab76ddb
Cleanup
anuraaga ae73969
Setup fixed credentials for dryrun mode
anuraaga fc9a99f
Cleanup
anuraaga 8aaf08c
regenerate src/semconv.ts with .../scripts/gen-semconv-ts.js (sorted …
trentm bde108c
briefly mention the custom bedrock-runtime instr in the README
trentm fa12583
drop the skip-test-if support added in #2723 because it is no longer …
trentm c1fc1f6
let's *not* remove the skip-test-if.js script even though it isn't cu…
trentm c220322
Merge branch 'main' into aws-bedrock-extension
trentm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
plugins/node/opentelemetry-instrumentation-aws-sdk/src/semconv.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
// Gen AI conventions | ||
|
||
export const ATTR_GEN_AI_SYSTEM = 'gen_ai.system'; | ||
export const ATTR_GEN_AI_OPERATION_NAME = 'gen_ai.operation.name'; | ||
export const ATTR_GEN_AI_REQUEST_MODEL = 'gen_ai.request.model'; | ||
export const ATTR_GEN_AI_REQUEST_MAX_TOKENS = 'gen_ai.request.max_tokens'; | ||
export const ATTR_GEN_AI_REQUEST_TEMPERATURE = 'gen_ai.request.temperature'; | ||
export const ATTR_GEN_AI_REQUEST_TOP_P = 'gen_ai.request.top_p'; | ||
export const ATTR_GEN_AI_REQUEST_STOP_SEQUENCES = | ||
'gen_ai.request.stop_sequences'; | ||
export const ATTR_GEN_AI_USAGE_INPUT_TOKENS = 'gen_ai.usage.input_tokens'; | ||
export const ATTR_GEN_AI_USAGE_OUTPUT_TOKENS = 'gen_ai.usage.output_tokens'; | ||
export const ATTR_GEN_AI_RESPONSE_FINISH_REASONS = | ||
'gen_ai.response.finish_reasons'; | ||
|
||
export const GEN_AI_SYSTEM_VALUE_AWS_BEDROCK = 'aws.bedrock'; | ||
export const GEN_AI_OPERATION_NAME_VALUE_CHAT = 'chat'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
112 changes: 112 additions & 0 deletions
112
plugins/node/opentelemetry-instrumentation-aws-sdk/src/services/bedrock-runtime.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,112 @@ | ||||||||||||||||||||||||||||||||||
/* | ||||||||||||||||||||||||||||||||||
* Copyright The OpenTelemetry Authors | ||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||||||||||||||||||||||||||||||||||
* you may not use this file except in compliance with the License. | ||||||||||||||||||||||||||||||||||
* You may obtain a copy of the License at | ||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||
* https://www.apache.org/licenses/LICENSE-2.0 | ||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||
* Unless required by applicable law or agreed to in writing, software | ||||||||||||||||||||||||||||||||||
* distributed under the License is distributed on an "AS IS" BASIS, | ||||||||||||||||||||||||||||||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||||||||||||||||||||||||||||||
* See the License for the specific language governing permissions and | ||||||||||||||||||||||||||||||||||
* limitations under the License. | ||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||
import { Attributes, DiagLogger, Span, Tracer } from '@opentelemetry/api'; | ||||||||||||||||||||||||||||||||||
import { RequestMetadata, ServiceExtension } from './ServiceExtension'; | ||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||
ATTR_GEN_AI_SYSTEM, | ||||||||||||||||||||||||||||||||||
ATTR_GEN_AI_OPERATION_NAME, | ||||||||||||||||||||||||||||||||||
ATTR_GEN_AI_REQUEST_MODEL, | ||||||||||||||||||||||||||||||||||
ATTR_GEN_AI_REQUEST_MAX_TOKENS, | ||||||||||||||||||||||||||||||||||
ATTR_GEN_AI_REQUEST_TEMPERATURE, | ||||||||||||||||||||||||||||||||||
ATTR_GEN_AI_REQUEST_TOP_P, | ||||||||||||||||||||||||||||||||||
ATTR_GEN_AI_REQUEST_STOP_SEQUENCES, | ||||||||||||||||||||||||||||||||||
ATTR_GEN_AI_USAGE_INPUT_TOKENS, | ||||||||||||||||||||||||||||||||||
ATTR_GEN_AI_USAGE_OUTPUT_TOKENS, | ||||||||||||||||||||||||||||||||||
ATTR_GEN_AI_RESPONSE_FINISH_REASONS, | ||||||||||||||||||||||||||||||||||
GEN_AI_OPERATION_NAME_VALUE_CHAT, | ||||||||||||||||||||||||||||||||||
GEN_AI_SYSTEM_VALUE_AWS_BEDROCK, | ||||||||||||||||||||||||||||||||||
} from '../semconv'; | ||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||
AwsSdkInstrumentationConfig, | ||||||||||||||||||||||||||||||||||
NormalizedRequest, | ||||||||||||||||||||||||||||||||||
NormalizedResponse, | ||||||||||||||||||||||||||||||||||
} from '../types'; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
export class BedrockRuntimeServiceExtension implements ServiceExtension { | ||||||||||||||||||||||||||||||||||
requestPreSpanHook( | ||||||||||||||||||||||||||||||||||
request: NormalizedRequest, | ||||||||||||||||||||||||||||||||||
config: AwsSdkInstrumentationConfig, | ||||||||||||||||||||||||||||||||||
diag: DiagLogger | ||||||||||||||||||||||||||||||||||
): RequestMetadata { | ||||||||||||||||||||||||||||||||||
let spanName: string | undefined; | ||||||||||||||||||||||||||||||||||
const spanAttributes: Attributes = { | ||||||||||||||||||||||||||||||||||
[ATTR_GEN_AI_SYSTEM]: GEN_AI_SYSTEM_VALUE_AWS_BEDROCK, | ||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
switch (request.commandName) { | ||||||||||||||||||||||||||||||||||
case 'Converse': | ||||||||||||||||||||||||||||||||||
spanAttributes[ATTR_GEN_AI_OPERATION_NAME] = | ||||||||||||||||||||||||||||||||||
GEN_AI_OPERATION_NAME_VALUE_CHAT; | ||||||||||||||||||||||||||||||||||
spanName = GEN_AI_OPERATION_NAME_VALUE_CHAT; | ||||||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
const modelId = request.commandInput.modelId; | ||||||||||||||||||||||||||||||||||
if (modelId) { | ||||||||||||||||||||||||||||||||||
spanAttributes[ATTR_GEN_AI_REQUEST_MODEL] = modelId; | ||||||||||||||||||||||||||||||||||
if (spanName) { | ||||||||||||||||||||||||||||||||||
spanName += ` ${modelId}`; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
const inferenceConfig = request.commandInput.inferenceConfig; | ||||||||||||||||||||||||||||||||||
if (inferenceConfig) { | ||||||||||||||||||||||||||||||||||
const { maxTokens, temperature, topP, stopSequences } = inferenceConfig; | ||||||||||||||||||||||||||||||||||
if (maxTokens !== undefined) { | ||||||||||||||||||||||||||||||||||
spanAttributes[ATTR_GEN_AI_REQUEST_MAX_TOKENS] = maxTokens; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
if (temperature !== undefined) { | ||||||||||||||||||||||||||||||||||
spanAttributes[ATTR_GEN_AI_REQUEST_TEMPERATURE] = temperature; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
if (topP !== undefined) { | ||||||||||||||||||||||||||||||||||
spanAttributes[ATTR_GEN_AI_REQUEST_TOP_P] = topP; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
if (stopSequences !== undefined) { | ||||||||||||||||||||||||||||||||||
spanAttributes[ATTR_GEN_AI_REQUEST_STOP_SEQUENCES] = stopSequences; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
return { | ||||||||||||||||||||||||||||||||||
spanName, | ||||||||||||||||||||||||||||||||||
isIncoming: false, | ||||||||||||||||||||||||||||||||||
spanAttributes, | ||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
responseHook( | ||||||||||||||||||||||||||||||||||
response: NormalizedResponse, | ||||||||||||||||||||||||||||||||||
span: Span, | ||||||||||||||||||||||||||||||||||
tracer: Tracer, | ||||||||||||||||||||||||||||||||||
config: AwsSdkInstrumentationConfig | ||||||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||||||
if (!span.isRecording()) { | ||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
const { stopReason, usage } = response.data; | ||||||||||||||||||||||||||||||||||
const { inputTokens, outputTokens } = usage; | ||||||||||||||||||||||||||||||||||
if (inputTokens !== undefined) { | ||||||||||||||||||||||||||||||||||
span.setAttribute(ATTR_GEN_AI_USAGE_INPUT_TOKENS, inputTokens); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
if (outputTokens !== undefined) { | ||||||||||||||||||||||||||||||||||
span.setAttribute(ATTR_GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
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. In case
Suggested change
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
if (stopReason !== undefined) { | ||||||||||||||||||||||||||||||||||
span.setAttribute(ATTR_GEN_AI_RESPONSE_FINISH_REASONS, [stopReason]); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} |
149 changes: 149 additions & 0 deletions
149
plugins/node/opentelemetry-instrumentation-aws-sdk/test/bedrock-runtime.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
/** | ||
* These tests verify telemetry created against actual API responses | ||
* which can be difficult to mock for LLMs. The responses are recorded | ||
* automatically using nock's nock-back feature. Responses are recorded | ||
* to the mock-responses directory with the name of the test - by default | ||
* if a response is available for the current test it is used, and | ||
* otherwise a real request is made and the response is recorded. | ||
* To re-record all responses, set the NOCK_BACK_MODE environment variable | ||
* to 'update' - when recording responses, valid AWS credentials for | ||
* accessing bedrock are also required. | ||
*/ | ||
|
||
import { | ||
getTestSpans, | ||
registerInstrumentationTesting, | ||
} from '@opentelemetry/contrib-test-utils'; | ||
import { AwsInstrumentation } from '../src'; | ||
registerInstrumentationTesting(new AwsInstrumentation()); | ||
|
||
import { | ||
BedrockRuntimeClient, | ||
ConverseCommand, | ||
ConversationRole, | ||
} from '@aws-sdk/client-bedrock-runtime'; | ||
import * as path from 'path'; | ||
import { Definition, back as nockBack } from 'nock'; | ||
|
||
import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; | ||
import { | ||
ATTR_GEN_AI_SYSTEM, | ||
ATTR_GEN_AI_OPERATION_NAME, | ||
ATTR_GEN_AI_REQUEST_MODEL, | ||
ATTR_GEN_AI_REQUEST_MAX_TOKENS, | ||
ATTR_GEN_AI_REQUEST_TEMPERATURE, | ||
ATTR_GEN_AI_REQUEST_TOP_P, | ||
ATTR_GEN_AI_REQUEST_STOP_SEQUENCES, | ||
ATTR_GEN_AI_USAGE_INPUT_TOKENS, | ||
ATTR_GEN_AI_USAGE_OUTPUT_TOKENS, | ||
ATTR_GEN_AI_RESPONSE_FINISH_REASONS, | ||
GEN_AI_SYSTEM_VALUE_AWS_BEDROCK, | ||
GEN_AI_OPERATION_NAME_VALUE_CHAT, | ||
} from '../src/semconv'; | ||
import { expect } from 'expect'; | ||
|
||
const region = 'us-east-1'; | ||
|
||
// Remove any data from recorded responses that could have sensitive data | ||
// and that we don't need for testing. | ||
const sanitizeRecordings = (scopes: Definition[]) => { | ||
for (const scope of scopes) { | ||
// Type definition seems to be incorrect of headers. | ||
const headers: string[] = (scope as any).rawHeaders; | ||
for (let i = 0; i < headers.length; i += 2) { | ||
if (headers[i].toLowerCase() === 'set-cookie') { | ||
headers.splice(i, 2); | ||
} | ||
} | ||
} | ||
return scopes; | ||
}; | ||
|
||
describe('Bedrock', () => { | ||
const client = new BedrockRuntimeClient({ region }); | ||
|
||
nockBack.fixtures = path.join(__dirname, 'mock-responses'); | ||
if (!process.env.NOCK_BACK_MODE) { | ||
nockBack.setMode('record'); | ||
} | ||
|
||
let nockDone: () => void; | ||
beforeEach(async function () { | ||
const filename = `${this.currentTest | ||
?.fullTitle() | ||
.toLowerCase() | ||
.replace(/\s/g, '-')}.json`; | ||
const { nockDone: nd } = await nockBack(filename, { | ||
afterRecord: sanitizeRecordings, | ||
}); | ||
nockDone = nd; | ||
}); | ||
|
||
afterEach(async function () { | ||
nockDone(); | ||
}); | ||
|
||
describe('Converse', () => { | ||
it('adds genai conventions', async () => { | ||
const modelId = 'amazon.titan-text-lite-v1'; | ||
const messages = [ | ||
{ | ||
role: ConversationRole.USER, | ||
content: [{ text: 'Say this is a test' }], | ||
}, | ||
]; | ||
const inferenceConfig = { | ||
maxTokens: 10, | ||
temperature: 0.8, | ||
topP: 1, | ||
stopSequences: ['|'], | ||
}; | ||
|
||
const command = new ConverseCommand({ | ||
modelId, | ||
messages, | ||
inferenceConfig, | ||
}); | ||
const response = await client.send(command); | ||
expect(response.output?.message?.content?.[0].text).toBe( | ||
"Hi. I'm not sure what" | ||
); | ||
|
||
const testSpans: ReadableSpan[] = getTestSpans(); | ||
const converseSpans: ReadableSpan[] = testSpans.filter( | ||
(s: ReadableSpan) => { | ||
return s.name === 'chat amazon.titan-text-lite-v1'; | ||
} | ||
); | ||
expect(converseSpans.length).toBe(1); | ||
expect(converseSpans[0].attributes).toMatchObject({ | ||
[ATTR_GEN_AI_SYSTEM]: GEN_AI_SYSTEM_VALUE_AWS_BEDROCK, | ||
[ATTR_GEN_AI_OPERATION_NAME]: GEN_AI_OPERATION_NAME_VALUE_CHAT, | ||
[ATTR_GEN_AI_REQUEST_MODEL]: modelId, | ||
[ATTR_GEN_AI_REQUEST_MAX_TOKENS]: 10, | ||
[ATTR_GEN_AI_REQUEST_TEMPERATURE]: 0.8, | ||
[ATTR_GEN_AI_REQUEST_TOP_P]: 1, | ||
[ATTR_GEN_AI_REQUEST_STOP_SEQUENCES]: ['|'], | ||
[ATTR_GEN_AI_USAGE_INPUT_TOKENS]: 8, | ||
[ATTR_GEN_AI_USAGE_OUTPUT_TOKENS]: 10, | ||
[ATTR_GEN_AI_RESPONSE_FINISH_REASONS]: ['max_tokens'], | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Can you also help copy over comments for the copied
ATTR_*
attribute keys from the semantic-conventions?Example from a resource-detector package
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.
I have a commit that re-generates the src/semconv.ts using a coming script for this (from #2669), if you are okay with me pushing to your branch, @anuraaga.
It also updates the semconv dep to
^1.29.0
because you are using semconv attributes defined in that version (GEN_AI_SYSTEM_VALUE_AWS_BEDROCK
) -- though technically because a copy (in src/semconv.ts) is being used, there isn't really the runtime dep.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.
Thanks @trentm - feel free to push anything you need to this branch