11import { beforeEach , describe , expect , it , vi } from 'vitest' ;
22import {
3- GEN_AI_EMBEDDINGS_OPERATION_ATTRIBUTE ,
43 GEN_AI_EMBEDDINGS_INPUT_ATTRIBUTE ,
4+ GEN_AI_EMBEDDINGS_OPERATION_ATTRIBUTE ,
55 GEN_AI_OPERATION_NAME_ATTRIBUTE ,
66 GEN_AI_REQUEST_DIMENSIONS_ATTRIBUTE ,
77 GEN_AI_REQUEST_ENCODING_FORMAT_ATTRIBUTE ,
@@ -10,29 +10,22 @@ import {
1010} from '../../../src/tracing/ai/gen-ai-attributes' ;
1111import { instrumentEmbeddingMethod , instrumentLangChainEmbeddings } from '../../../src/tracing/langchain/embeddings' ;
1212
13- // Mock resolveAIRecordingOptions to control recordInputs
1413vi . mock ( '../../../src/tracing/ai/utils' , ( ) => ( {
1514 resolveAIRecordingOptions : ( options : { recordInputs ?: boolean ; recordOutputs ?: boolean } = { } ) => ( {
1615 recordInputs : options . recordInputs ?? false ,
1716 recordOutputs : options . recordOutputs ?? false ,
1817 } ) ,
1918} ) ) ;
2019
21- // Capture span data from startSpan calls
2220let capturedSpanConfig : { name : string ; op : string ; attributes : Record < string , unknown > } | undefined ;
23- let capturedSpanSetStatus : ReturnType < typeof vi . fn > ;
2421
2522vi . mock ( '../../../src/tracing/trace' , ( ) => ( {
2623 startSpan : (
2724 config : { name : string ; op : string ; attributes : Record < string , unknown > } ,
2825 callback : ( span : unknown ) => unknown ,
2926 ) => {
3027 capturedSpanConfig = config ;
31- const mockSpan = {
32- setStatus : capturedSpanSetStatus ,
33- setAttribute : vi . fn ( ) ,
34- } ;
35- return callback ( mockSpan ) ;
28+ return callback ( { setAttribute : vi . fn ( ) } ) ;
3629 } ,
3730} ) ) ;
3831
@@ -45,14 +38,18 @@ vi.mock('../../../src/exports', () => ({
4538describe ( 'instrumentEmbeddingMethod' , ( ) => {
4639 beforeEach ( ( ) => {
4740 capturedSpanConfig = undefined ;
48- capturedSpanSetStatus = vi . fn ( ) ;
4941 } ) ;
5042
51- it ( 'creates a span with correct attributes for embedQuery ' , async ( ) => {
43+ it ( 'creates a span with correct attributes' , async ( ) => {
5244 const original = vi . fn ( ) . mockResolvedValue ( [ 0.1 , 0.2 , 0.3 ] ) ;
5345 const wrapped = instrumentEmbeddingMethod ( original ) ;
5446
55- const instance = { constructor : { name : 'OpenAIEmbeddings' } , model : 'text-embedding-3-small' , dimensions : 1536 } ;
47+ const instance = {
48+ constructor : { name : 'OpenAIEmbeddings' } ,
49+ model : 'text-embedding-3-small' ,
50+ dimensions : 1536 ,
51+ encodingFormat : 'float' ,
52+ } ;
5653 await wrapped . call ( instance , 'Hello world' ) ;
5754
5855 expect ( capturedSpanConfig ) . toBeDefined ( ) ;
@@ -62,43 +59,23 @@ describe('instrumentEmbeddingMethod', () => {
6259 expect ( capturedSpanConfig ! . attributes [ GEN_AI_REQUEST_MODEL_ATTRIBUTE ] ) . toBe ( 'text-embedding-3-small' ) ;
6360 expect ( capturedSpanConfig ! . attributes [ GEN_AI_SYSTEM_ATTRIBUTE ] ) . toBe ( 'openai' ) ;
6461 expect ( capturedSpanConfig ! . attributes [ GEN_AI_REQUEST_DIMENSIONS_ATTRIBUTE ] ) . toBe ( 1536 ) ;
65- expect ( original ) . toHaveBeenCalledWith ( 'Hello world' ) ;
66- } ) ;
67-
68- it ( 'creates a span with correct attributes for embedDocuments' , async ( ) => {
69- const original = vi . fn ( ) . mockResolvedValue ( [ [ 0.1 ] , [ 0.2 ] ] ) ;
70- const wrapped = instrumentEmbeddingMethod ( original ) ;
71-
72- const instance = { constructor : { name : 'MistralAIEmbeddings' } , model : 'mistral-embed' , encodingFormat : 'float' } ;
73- await wrapped . call ( instance , [ 'doc1' , 'doc2' ] ) ;
74-
75- expect ( capturedSpanConfig ! . name ) . toBe ( 'embeddings mistral-embed' ) ;
76- expect ( capturedSpanConfig ! . attributes [ GEN_AI_OPERATION_NAME_ATTRIBUTE ] ) . toBe ( 'embeddings' ) ;
77- expect ( capturedSpanConfig ! . attributes [ GEN_AI_SYSTEM_ATTRIBUTE ] ) . toBe ( 'mistralai' ) ;
7862 expect ( capturedSpanConfig ! . attributes [ GEN_AI_REQUEST_ENCODING_FORMAT_ATTRIBUTE ] ) . toBe ( 'float' ) ;
63+ expect ( original ) . toHaveBeenCalledWith ( 'Hello world' ) ;
7964 } ) ;
8065
81- it ( 'records input when recordInputs is true (string) ' , async ( ) => {
66+ it ( 'records input when recordInputs is true' , async ( ) => {
8267 const original = vi . fn ( ) . mockResolvedValue ( [ 0.1 ] ) ;
83- const wrapped = instrumentEmbeddingMethod ( original , { recordInputs : true } ) ;
84-
8568 const instance = { constructor : { name : 'OpenAIEmbeddings' } , model : 'text-embedding-3-small' } ;
86- await wrapped . call ( instance , 'Hello world' ) ;
87-
88- expect ( capturedSpanConfig ! . attributes [ GEN_AI_EMBEDDINGS_INPUT_ATTRIBUTE ] ) . toBe ( 'Hello world' ) ;
89- } ) ;
9069
91- it ( 'records input when recordInputs is true (array)' , async ( ) => {
92- const original = vi . fn ( ) . mockResolvedValue ( [ [ 0.1 ] ] ) ;
9370 const wrapped = instrumentEmbeddingMethod ( original , { recordInputs : true } ) ;
71+ await wrapped . call ( instance , 'Hello world' ) ;
72+ expect ( capturedSpanConfig ! . attributes [ GEN_AI_EMBEDDINGS_INPUT_ATTRIBUTE ] ) . toBe ( 'Hello world' ) ;
9473
95- const instance = { constructor : { name : 'OpenAIEmbeddings' } , model : 'text-embedding-3-small' } ;
9674 await wrapped . call ( instance , [ 'doc1' , 'doc2' ] ) ;
97-
9875 expect ( capturedSpanConfig ! . attributes [ GEN_AI_EMBEDDINGS_INPUT_ATTRIBUTE ] ) . toBe ( '["doc1","doc2"]' ) ;
9976 } ) ;
10077
101- it ( 'sets error status on failure' , async ( ) => {
78+ it ( 'captures exception on failure' , async ( ) => {
10279 const error = new Error ( 'API error' ) ;
10380 const original = vi . fn ( ) . mockRejectedValue ( error ) ;
10481 const wrapped = instrumentEmbeddingMethod ( original ) ;
@@ -111,24 +88,12 @@ describe('instrumentEmbeddingMethod', () => {
11188 } ) ;
11289 } ) ;
11390
114- it ( 'infers system from various class names ' , async ( ) => {
91+ it ( 'infers system from class name ' , async ( ) => {
11592 const original = vi . fn ( ) . mockResolvedValue ( [ 0.1 ] ) ;
93+ const wrapped = instrumentEmbeddingMethod ( original ) ;
11694
117- const testCases = [
118- { className : 'GoogleGenerativeAIEmbeddings' , expected : 'google_genai' } ,
119- { className : 'VertexAIEmbeddings' , expected : 'google_vertexai' } ,
120- { className : 'BedrockEmbeddings' , expected : 'aws_bedrock' } ,
121- { className : 'OllamaEmbeddings' , expected : 'ollama' } ,
122- { className : 'CloudflareWorkersAIEmbeddings' , expected : 'cloudflare' } ,
123- ] ;
124-
125- for ( const { className, expected } of testCases ) {
126- const wrapped = instrumentEmbeddingMethod ( original ) ;
127- const instance = { constructor : { name : className } , model : 'test-model' } ;
128- await wrapped . call ( instance , 'test' ) ;
129-
130- expect ( capturedSpanConfig ! . attributes [ GEN_AI_SYSTEM_ATTRIBUTE ] ) . toBe ( expected ) ;
131- }
95+ await wrapped . call ( { constructor : { name : 'GoogleGenerativeAIEmbeddings' } , model : 'test' } , 'test' ) ;
96+ expect ( capturedSpanConfig ! . attributes [ GEN_AI_SYSTEM_ATTRIBUTE ] ) . toBe ( 'google_genai' ) ;
13297 } ) ;
13398
13499 it ( 'handles missing instance properties gracefully' , async ( ) => {
@@ -147,22 +112,18 @@ describe('instrumentEmbeddingMethod', () => {
147112describe ( 'instrumentLangChainEmbeddings' , ( ) => {
148113 beforeEach ( ( ) => {
149114 capturedSpanConfig = undefined ;
150- capturedSpanSetStatus = vi . fn ( ) ;
151115 } ) ;
152116
153117 it ( 'wraps both embedQuery and embedDocuments on an instance' , async ( ) => {
154- const mockEmbedQuery = vi . fn ( ) . mockResolvedValue ( [ 0.1 ] ) ;
155- const mockEmbedDocuments = vi . fn ( ) . mockResolvedValue ( [ [ 0.1 ] ] ) ;
156-
157118 const instance = {
158119 constructor : { name : 'OpenAIEmbeddings' } ,
159120 model : 'text-embedding-3-small' ,
160- embedQuery : mockEmbedQuery ,
161- embedDocuments : mockEmbedDocuments ,
121+ embedQuery : vi . fn ( ) . mockResolvedValue ( [ 0.1 ] ) ,
122+ embedDocuments : vi . fn ( ) . mockResolvedValue ( [ [ 0.1 ] ] ) ,
162123 } ;
163124
164125 const wrapped = instrumentLangChainEmbeddings ( instance ) ;
165- expect ( wrapped ) . toBe ( instance ) ; // Returns the same instance
126+ expect ( wrapped ) . toBe ( instance ) ;
166127
167128 await wrapped . embedQuery ( 'test' ) ;
168129 expect ( capturedSpanConfig ! . attributes [ GEN_AI_OPERATION_NAME_ATTRIBUTE ] ) . toBe ( 'embeddings' ) ;
0 commit comments