Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/sdk/server-ai/__tests__/LDAIClientImpl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ it('returns config with interpolated messagess', async () => {
],
tracker: expect.any(Object),
enabled: true,
toVercelAISDK: expect.any(Function),
});
});

Expand Down Expand Up @@ -102,6 +103,7 @@ it('handles missing metadata in variation', async () => {
messages: [{ role: 'system', content: 'Hello' }],
tracker: expect.any(Object),
enabled: false,
toVercelAISDK: expect.any(Function),
});
});

Expand All @@ -125,6 +127,7 @@ it('passes the default value to the underlying client', async () => {
provider: defaultValue.provider,
tracker: expect.any(Object),
enabled: false,
toVercelAISDK: expect.any(Function),
});

expect(mockLdClient.variation).toHaveBeenCalledWith(key, testContext, defaultValue);
Expand Down
159 changes: 159 additions & 0 deletions packages/sdk/server-ai/__tests__/LDAIConfigMapper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { LDMessage, VercelAISDKMapOptions } from '../src/api/config';
import { LDAIConfigMapper } from '../src/LDAIConfigMapper';

describe('_findParameter', () => {
it('handles undefined model and messages', () => {
const mapper = new LDAIConfigMapper();
// eslint-disable-next-line @typescript-eslint/dot-notation
expect(mapper['_findParameter']<number>('test-param')).toBeUndefined();
});

it('handles parameter not found', () => {
const mapper = new LDAIConfigMapper({
name: 'test-ai-model',
parameters: {
'test-param': 123,
},
custom: {
'test-param': 456,
},
});
// eslint-disable-next-line @typescript-eslint/dot-notation
expect(mapper['_findParameter']<number>('other-param')).toBeUndefined();
});

it('finds parameter from single model parameter', () => {
const mapper = new LDAIConfigMapper({
name: 'test-ai-model',
parameters: {
'test-param': 123,
},
});
// eslint-disable-next-line @typescript-eslint/dot-notation
expect(mapper['_findParameter']<number>('test-param')).toEqual(123);
});

it('finds parameter from multiple model parameters', () => {
const mapper = new LDAIConfigMapper({
name: 'test-ai-model',
parameters: {
testParam: 123,
},
});
// eslint-disable-next-line @typescript-eslint/dot-notation
expect(mapper['_findParameter']<number>('test-param', 'testParam')).toEqual(123);
});

it('finds parameter from single model custom parameter', () => {
const mapper = new LDAIConfigMapper({
name: 'test-ai-model',
custom: {
'test-param': 123,
},
});
// eslint-disable-next-line @typescript-eslint/dot-notation
expect(mapper['_findParameter']<number>('test-param')).toEqual(123);
});

it('finds parameter from multiple model custom parameters', () => {
const mapper = new LDAIConfigMapper({
name: 'test-ai-model',
custom: {
testParam: 123,
},
});
// eslint-disable-next-line @typescript-eslint/dot-notation
expect(mapper['_findParameter']<number>('test-param', 'testParam')).toEqual(123);
});

it('gives precedence to model parameters over model custom parameters', () => {
const mapper = new LDAIConfigMapper({
name: 'test-ai-model',
parameters: {
'test-param': 123,
},
custom: {
'test-param': 456,
},
});
// eslint-disable-next-line @typescript-eslint/dot-notation
expect(mapper['_findParameter']<number>('test-param', 'testParam')).toEqual(123);
});
});

describe('toVercelAIAISDK', () => {
const mockModel = { name: 'mockModel' };
const mockMessages: LDMessage[] = [
{ role: 'user', content: 'test prompt' },
{ role: 'system', content: 'test instruction' },
];
const mockOptions: VercelAISDKMapOptions = {
nonInterpolatedMessages: [{ role: 'assistant', content: 'test assistant instruction' }],
};
const mockProvider = jest.fn().mockReturnValue(mockModel);

beforeEach(() => {
jest.clearAllMocks();
});

it('handles undefined model and messages', () => {
const mapper = new LDAIConfigMapper();
const result = mapper.toVercelAISDK(mockProvider);

expect(mockProvider).toHaveBeenCalledWith('');
expect(result).toEqual(
expect.objectContaining({
model: mockModel,
messages: undefined,
}),
);
});

it('uses additional messages', () => {
const mapper = new LDAIConfigMapper({ name: 'test-ai-model' });
const result = mapper.toVercelAISDK(mockProvider, mockOptions);

expect(mockProvider).toHaveBeenCalledWith('test-ai-model');
expect(result).toEqual(
expect.objectContaining({
model: mockModel,
messages: mockOptions.nonInterpolatedMessages,
}),
);
});

it('combines config messages and additional messages', () => {
const mapper = new LDAIConfigMapper({ name: 'test-ai-model' }, undefined, mockMessages);
const result = mapper.toVercelAISDK(mockProvider, mockOptions);

expect(mockProvider).toHaveBeenCalledWith('test-ai-model');
expect(result).toEqual(
expect.objectContaining({
model: mockModel,
messages: [...mockMessages, ...(mockOptions.nonInterpolatedMessages ?? [])],
}),
);
});

it('requests parameters correctly', () => {
const mapper = new LDAIConfigMapper({ name: 'test-ai-model' }, undefined, mockMessages);
const findParameterMock = jest.spyOn(mapper as any, '_findParameter');
const result = mapper.toVercelAISDK(mockProvider);

expect(mockProvider).toHaveBeenCalledWith('test-ai-model');
expect(result).toEqual(
expect.objectContaining({
model: mockModel,
messages: mockMessages,
}),
);
expect(findParameterMock).toHaveBeenCalledWith('max_tokens', 'maxTokens');
expect(findParameterMock).toHaveBeenCalledWith('temperature');
expect(findParameterMock).toHaveBeenCalledWith('top_p', 'topP');
expect(findParameterMock).toHaveBeenCalledWith('top_k', 'topK');
expect(findParameterMock).toHaveBeenCalledWith('presence_penalty', 'presencePenalty');
expect(findParameterMock).toHaveBeenCalledWith('frequency_penalty', 'frequencyPenalty');
expect(findParameterMock).toHaveBeenCalledWith('stop', 'stop_sequences', 'stopSequences');
expect(findParameterMock).toHaveBeenCalledWith('seed');
});
});
Loading