@beparallel/langchain-ts
Version:
Extracts Langchain prompts and generates TypeScript types.
193 lines (153 loc) • 6.68 kB
text/typescript
import { ChatPromptTemplate } from '@langchain/core/prompts'
import { pull } from 'langchain/hub'
import { Client } from 'langsmith'
import { extractPrompts } from './parser.js'
// Mock the external dependencies
jest.mock('langchain/hub')
jest.mock('langsmith')
const mockPull = jest.mocked(pull)
const MockClient = jest.mocked(Client)
describe('Parser Module', () => {
describe('extractPrompts', () => {
const mockApiKey = 'test-api-key'
const mockTag = 'test-tag'
let mockClientInstance: {
listPrompts: jest.MockedFunction<any>
}
beforeEach(() => {
jest.clearAllMocks()
// Create mock client instance
mockClientInstance = {
listPrompts: jest.fn(),
}
// Mock the Client constructor
MockClient.mockImplementation(() => mockClientInstance as any)
// Mock console methods
jest.spyOn(console, 'debug').mockImplementation(() => {})
jest.spyOn(console, 'error').mockImplementation(() => {})
})
afterEach(() => {
jest.restoreAllMocks()
})
test('should extract prompts successfully', async () => {
// Arrange
const mockPrompts = [{ repo_handle: 'prompt1' }, { repo_handle: 'prompt2' }]
const mockTemplate1 = {} as ChatPromptTemplate
const mockTemplate2 = {} as ChatPromptTemplate
mockClientInstance.listPrompts.mockResolvedValue(mockPrompts)
mockPull.mockResolvedValueOnce(mockTemplate1).mockResolvedValueOnce(mockTemplate2)
// Act
const result = await extractPrompts({
langchainApiKey: mockApiKey,
langchainTag: mockTag,
})
// Assert
expect(MockClient).toHaveBeenCalledWith({ apiKey: mockApiKey })
expect(mockClientInstance.listPrompts).toHaveBeenCalledWith({
isPublic: false,
isArchived: false,
sortField: 'updated_at',
})
expect(mockPull).toHaveBeenCalledTimes(2)
expect(mockPull).toHaveBeenNthCalledWith(1, 'prompt1:test-tag')
expect(mockPull).toHaveBeenNthCalledWith(2, 'prompt2:test-tag')
expect(result).toEqual([mockTemplate1, mockTemplate2])
})
test('should handle empty prompts list', async () => {
// Arrange
mockClientInstance.listPrompts.mockResolvedValue([])
// Act
const result = await extractPrompts({
langchainApiKey: mockApiKey,
langchainTag: mockTag,
})
// Assert
expect(result).toEqual([])
expect(mockPull).not.toHaveBeenCalled()
})
test('should handle hub.pull errors gracefully', async () => {
// Arrange
const mockPrompts = [{ repo_handle: 'prompt1' }, { repo_handle: 'prompt2' }, { repo_handle: 'prompt3' }]
const mockTemplate1 = {} as ChatPromptTemplate
const mockTemplate3 = {} as ChatPromptTemplate
mockClientInstance.listPrompts.mockResolvedValue(mockPrompts)
mockPull
.mockResolvedValueOnce(mockTemplate1)
.mockRejectedValueOnce(new Error('Failed to pull prompt2'))
.mockResolvedValueOnce(mockTemplate3)
// Act
const result = await extractPrompts({
langchainApiKey: mockApiKey,
langchainTag: mockTag,
})
// Assert
expect(result).toEqual([mockTemplate1, mockTemplate3])
expect(console.error).toHaveBeenCalledWith(new Error('Failed to pull prompt2'))
})
test('should handle Client.listPrompts error', async () => {
// Arrange
const error = new Error('API Error')
mockClientInstance.listPrompts.mockRejectedValue(error)
// Act & Assert
await expect(
extractPrompts({
langchainApiKey: mockApiKey,
langchainTag: mockTag,
}),
).rejects.toThrow('API Error')
})
test('should use correct parameters for Client initialization', async () => {
// Arrange
mockClientInstance.listPrompts.mockResolvedValue([])
// Act
await extractPrompts({
langchainApiKey: 'custom-key',
langchainTag: mockTag,
})
// Assert
expect(MockClient).toHaveBeenCalledWith({ apiKey: 'custom-key' })
})
test('should use correct tag in hub.pull calls', async () => {
// Arrange
const mockPrompts = [{ repo_handle: 'test-prompt' }]
const mockTemplate = {} as ChatPromptTemplate
mockClientInstance.listPrompts.mockResolvedValue(mockPrompts)
mockPull.mockResolvedValue(mockTemplate)
// Act
await extractPrompts({
langchainApiKey: mockApiKey,
langchainTag: 'custom-tag',
})
// Assert
expect(mockPull).toHaveBeenCalledWith('test-prompt:custom-tag')
})
test('should log debug messages for each prompt', async () => {
// Arrange
const mockPrompts = [{ repo_handle: 'prompt1' }, { repo_handle: 'prompt2' }]
mockClientInstance.listPrompts.mockResolvedValue(mockPrompts)
mockPull.mockResolvedValue({} as ChatPromptTemplate)
// Act
await extractPrompts({
langchainApiKey: mockApiKey,
langchainTag: mockTag,
})
// Assert
expect(console.debug).toHaveBeenCalledWith('Generating prompt prompt1')
expect(console.debug).toHaveBeenCalledWith('Generating prompt prompt2')
})
test('should handle all prompts failing to pull', async () => {
// Arrange
const mockPrompts = [{ repo_handle: 'prompt1' }, { repo_handle: 'prompt2' }]
mockClientInstance.listPrompts.mockResolvedValue(mockPrompts)
mockPull.mockRejectedValueOnce(new Error('Error 1')).mockRejectedValueOnce(new Error('Error 2'))
// Act
const result = await extractPrompts({
langchainApiKey: mockApiKey,
langchainTag: mockTag,
})
// Assert
expect(result).toEqual([])
expect(console.error).toHaveBeenCalledTimes(2)
})
})
})