Skip to content
This repository was archived by the owner on Aug 5, 2025. It is now read-only.

Commit 2cae4a1

Browse files
committed
feat: refactor and fix tests
1 parent 3dab603 commit 2cae4a1

File tree

5 files changed

+198
-67
lines changed

5 files changed

+198
-67
lines changed

src/api.ts

Lines changed: 7 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { ReadStream } from 'fs';
55
import { v4 as uuidv4 } from 'uuid';
66

77
import { LiteralClient } from '.';
8+
import { PromptCacheManager } from './cache/prompt-cache-manager';
9+
import { sharedCache } from './cache/sharedcache';
810
import {
911
Dataset,
1012
DatasetExperiment,
@@ -325,63 +327,6 @@ type CreateAttachmentParams = {
325327
metadata?: Maybe<Record<string, any>>;
326328
};
327329

328-
export class SharedCache {
329-
private static instance: SharedCache | null = null;
330-
private cache: Map<string, any>;
331-
332-
private constructor() {
333-
this.cache = new Map();
334-
}
335-
336-
static getInstance(): SharedCache {
337-
if (!SharedCache.instance) {
338-
SharedCache.instance = new SharedCache();
339-
}
340-
return SharedCache.instance;
341-
}
342-
343-
public getPromptCacheKey(
344-
id?: string,
345-
name?: string,
346-
version?: number
347-
): string {
348-
if (id) {
349-
return id;
350-
} else if (name && (version || version === 0)) {
351-
return `${name}:${version}`;
352-
} else if (name) {
353-
return name;
354-
}
355-
throw new Error('Either id or name must be provided');
356-
}
357-
358-
public getPrompt(key: string): Prompt {
359-
return this.get(key);
360-
}
361-
362-
public putPrompt(prompt: Prompt): void {
363-
this.put(prompt.id, prompt);
364-
this.put(prompt.name, prompt);
365-
this.put(`${prompt.name}:${prompt.version}`, prompt);
366-
}
367-
368-
public getCache(): Map<string, any> {
369-
return this.cache;
370-
}
371-
372-
public get(key: string): any {
373-
return this.cache.get(key);
374-
}
375-
376-
public put(key: string, value: any): void {
377-
this.cache.set(key, value);
378-
}
379-
380-
public clear(): void {
381-
this.cache.clear();
382-
}
383-
}
384-
385330
/**
386331
* Represents the API client for interacting with the Literal service.
387332
* This class handles API requests, authentication, and provides methods
@@ -398,7 +343,7 @@ export class SharedCache {
398343
*/
399344
export class API {
400345
/** @ignore */
401-
public cache: SharedCache;
346+
private cache: typeof sharedCache;
402347
/** @ignore */
403348
public client: LiteralClient;
404349
/** @ignore */
@@ -431,7 +376,7 @@ export class API {
431376
throw new Error('LITERAL_API_URL not set');
432377
}
433378

434-
this.cache = SharedCache.getInstance();
379+
this.cache = sharedCache;
435380

436381
this.apiKey = apiKey;
437382
this.url = url;
@@ -2210,8 +2155,8 @@ export class API {
22102155
variables: Record<string, any>
22112156
) {
22122157
const { id, name, version } = variables;
2213-
const cachedPrompt = this.cache.getPrompt(
2214-
this.cache.getPromptCacheKey(id, name, version)
2158+
const cachedPrompt = sharedCache.get(
2159+
PromptCacheManager.getPromptCacheKey({ id, name, version })
22152160
);
22162161
const timeout = cachedPrompt ? 1000 : undefined;
22172162

@@ -2231,11 +2176,9 @@ export class API {
22312176
}
22322177

22332178
const prompt = new Prompt(this, promptData);
2234-
this.cache.putPrompt(prompt);
2179+
PromptCacheManager.putPrompt(prompt);
22352180
return prompt;
22362181
} catch (error) {
2237-
console.log('key: ', this.cache.getPromptCacheKey(id, name, version));
2238-
console.log('cachedPrompt: ', cachedPrompt);
22392182
return cachedPrompt;
22402183
}
22412184
}

src/cache/prompt-cache-manager.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Prompt } from '../prompt-engineering/prompt';
2+
import { sharedCache } from './sharedcache';
3+
4+
export class PromptCacheManager {
5+
public static getPromptCacheKey({
6+
id,
7+
name,
8+
version
9+
}: {
10+
id?: string;
11+
name?: string;
12+
version?: number;
13+
}): string {
14+
if (id) {
15+
return id;
16+
} else if (name && typeof version === 'number') {
17+
return `${name}:${version}`;
18+
} else if (name) {
19+
return name;
20+
}
21+
throw new Error('Either id or name must be provided');
22+
}
23+
24+
public static putPrompt(prompt: Prompt): void {
25+
sharedCache.put(prompt.id, prompt);
26+
sharedCache.put(prompt.name, prompt);
27+
sharedCache.put(`${prompt.name}:${prompt.version}`, prompt);
28+
}
29+
}

src/cache/sharedcache.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const cache: Map<string, any> = new Map();
2+
3+
class SharedCache {
4+
private static instance: SharedCache;
5+
6+
public constructor() {
7+
if (SharedCache.instance) {
8+
throw new Error('SharedCache can only be created once');
9+
}
10+
SharedCache.instance = this;
11+
}
12+
13+
public getInstance(): SharedCache {
14+
return this;
15+
}
16+
17+
public getCache(): Map<string, any> {
18+
return cache;
19+
}
20+
21+
public get(key: string): any {
22+
return cache.get(key);
23+
}
24+
25+
public put(key: string, value: any): void {
26+
cache.set(key, value);
27+
}
28+
29+
public clear(): void {
30+
cache.clear();
31+
}
32+
}
33+
34+
export const sharedCache = new SharedCache();
35+
36+
export default sharedCache;

tests/api.test.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import 'dotenv/config';
33
import { v4 as uuidv4 } from 'uuid';
44

55
import { ChatGeneration, IGenerationMessage, LiteralClient } from '../src';
6+
import { PromptCacheManager } from '../src/cache/prompt-cache-manager';
7+
import { sharedCache } from '../src/cache/sharedcache';
68
import { Dataset } from '../src/evaluation/dataset';
79
import { Score } from '../src/evaluation/score';
810
import { Prompt, PromptConstructor } from '../src/prompt-engineering/prompt';
@@ -685,7 +687,7 @@ is a templated list.`;
685687

686688
it('should fallback to cache when getPromptById DB call fails', async () => {
687689
const prompt = new Prompt(client.api, mockPromptData);
688-
client.api.cache.putPrompt(prompt);
690+
PromptCacheManager.putPrompt(prompt);
689691

690692
jest
691693
.spyOn(client.api as any, 'makeGqlCall')
@@ -697,7 +699,7 @@ is a templated list.`;
697699

698700
it('should fallback to cache when getPrompt DB call fails', async () => {
699701
const prompt = new Prompt(client.api, mockPromptData);
700-
client.api.cache.putPrompt(prompt);
702+
PromptCacheManager.putPrompt(prompt);
701703
jest.spyOn(axios, 'post').mockRejectedValueOnce(new Error('DB Error'));
702704

703705
const result = await client.api.getPrompt(prompt.id);
@@ -713,7 +715,7 @@ is a templated list.`;
713715

714716
await client.api.getPromptById(prompt.id);
715717

716-
const cachedPrompt = await client.api.cache.get(prompt.id);
718+
const cachedPrompt = sharedCache.get(prompt.id);
717719
expect(cachedPrompt).toBeDefined();
718720
expect(cachedPrompt?.id).toBe(prompt.id);
719721
});

tests/cache.test.ts

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { API } from '../src/api';
2+
import { PromptCacheManager } from '../src/cache/prompt-cache-manager';
3+
import { sharedCache } from '../src/cache/sharedcache';
4+
import { Prompt, PromptConstructor } from '../src/prompt-engineering/prompt';
5+
6+
describe('Cache', () => {
7+
let api: API;
8+
let mockPrompt: Prompt;
9+
10+
beforeAll(() => {
11+
api = {} as API;
12+
});
13+
14+
beforeEach(() => {
15+
sharedCache.clear();
16+
17+
const mockPromptData: PromptConstructor = {
18+
id: 'test-id',
19+
type: 'CHAT',
20+
createdAt: '2023-01-01T00:00:00Z',
21+
name: 'test-name',
22+
version: 1,
23+
metadata: {},
24+
items: [],
25+
templateMessages: [{ role: 'user', content: 'Hello', uuid: '123' }],
26+
provider: 'test-provider',
27+
settings: {
28+
provider: 'test-provider',
29+
model: 'test-model',
30+
frequency_penalty: 0,
31+
max_tokens: 100,
32+
presence_penalty: 0,
33+
temperature: 0.7,
34+
top_p: 1
35+
},
36+
variables: []
37+
};
38+
mockPrompt = new Prompt(api, mockPromptData);
39+
});
40+
41+
describe('PromptCacheManager', () => {
42+
describe('getPromptCacheKey', () => {
43+
it('should return id when provided', () => {
44+
const key = PromptCacheManager.getPromptCacheKey({
45+
id: 'test-id',
46+
name: 'test-name',
47+
version: 1
48+
});
49+
expect(key).toBe('test-id');
50+
});
51+
52+
it('should return name:version when id not provided but name and version are', () => {
53+
const key = PromptCacheManager.getPromptCacheKey({
54+
name: 'test-name',
55+
version: 1
56+
});
57+
expect(key).toBe('test-name:1');
58+
});
59+
60+
it('should return name when only name provided', () => {
61+
const key = PromptCacheManager.getPromptCacheKey({ name: 'test-name' });
62+
expect(key).toBe('test-name');
63+
});
64+
65+
it('should throw error when neither id nor name provided', () => {
66+
expect(() =>
67+
PromptCacheManager.getPromptCacheKey({ version: 0 })
68+
).toThrow('Either id or name must be provided');
69+
});
70+
});
71+
72+
describe('putPrompt', () => {
73+
it('should store prompt with multiple keys', () => {
74+
PromptCacheManager.putPrompt(mockPrompt);
75+
76+
expect(sharedCache.get('test-id')).toEqual(mockPrompt);
77+
expect(sharedCache.get('test-name')).toEqual(mockPrompt);
78+
expect(sharedCache.get('test-name:1')).toEqual(mockPrompt);
79+
});
80+
});
81+
});
82+
83+
describe('SharedCache', () => {
84+
it('should return undefined for non-existent key', () => {
85+
const value = sharedCache.get('non-existent');
86+
expect(value).toBeUndefined();
87+
});
88+
89+
it('should store and retrieve values', () => {
90+
sharedCache.put('test-key', 'test-value');
91+
expect(sharedCache.get('test-key')).toBe('test-value');
92+
});
93+
94+
it('should clear all values', () => {
95+
sharedCache.put('key1', 'value1');
96+
sharedCache.put('key2', 'value2');
97+
98+
sharedCache.clear();
99+
100+
expect(sharedCache.get('key1')).toBeUndefined();
101+
expect(sharedCache.get('key2')).toBeUndefined();
102+
});
103+
104+
it('should maintain singleton behavior', () => {
105+
const instance1 = sharedCache;
106+
const instance2 = sharedCache;
107+
108+
instance1.put('test', 'value');
109+
expect(instance2.get('test')).toBe('value');
110+
expect(instance1).toBe(instance2);
111+
});
112+
113+
it('should expose cache map', () => {
114+
sharedCache.put('test', 'value');
115+
const cacheMap = sharedCache.getCache();
116+
117+
expect(cacheMap instanceof Map).toBe(true);
118+
expect(cacheMap.get('test')).toBe('value');
119+
});
120+
});
121+
});

0 commit comments

Comments
 (0)