Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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