Skip to content

Commit 134eeb0

Browse files
authored
Fix: Correctly use context model for moderation (#26)
* Correctly use context model for moderation * Address copilot comments
1 parent 24da084 commit 134eeb0

File tree

2 files changed

+80
-1
lines changed

2 files changed

+80
-1
lines changed

src/__tests__/unit/checks/moderation-secret-keys.test.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,86 @@ describe('moderation guardrail', () => {
6767
expect(result.tripwireTriggered).toBe(false);
6868
expect(result.info?.error).toBe('Moderation API call failed');
6969
});
70+
71+
it('uses context client when available', async () => {
72+
// Track whether context client was used
73+
let contextClientUsed = false;
74+
const contextCreateMock = vi.fn().mockImplementation(async () => {
75+
contextClientUsed = true;
76+
return {
77+
results: [
78+
{
79+
categories: {
80+
[Category.HATE]: false,
81+
[Category.VIOLENCE]: false,
82+
},
83+
},
84+
],
85+
};
86+
});
87+
88+
// Create a context with a guardrailLlm client
89+
// We need to import OpenAI to create a proper instance
90+
const OpenAI = (await import('openai')).default;
91+
const contextClient = new OpenAI({ apiKey: 'test-context-key' });
92+
contextClient.moderations = {
93+
create: contextCreateMock,
94+
} as unknown as typeof contextClient.moderations;
95+
96+
const ctx = { guardrailLlm: contextClient };
97+
const cfg = ModerationConfig.parse({ categories: [Category.HATE] });
98+
const result = await moderationCheck(ctx, 'test text', cfg);
99+
100+
// Verify the context client was used
101+
expect(contextClientUsed).toBe(true);
102+
expect(contextCreateMock).toHaveBeenCalledWith({
103+
model: 'omni-moderation-latest',
104+
input: 'test text',
105+
safety_identifier: 'openai-guardrails-js',
106+
});
107+
expect(result.tripwireTriggered).toBe(false);
108+
});
109+
110+
it('falls back to default client for third-party providers', async () => {
111+
// Track whether fallback client was used
112+
let fallbackUsed = false;
113+
114+
// The default mock from vi.mock will be used for the fallback
115+
createMock.mockImplementation(async () => {
116+
fallbackUsed = true;
117+
return {
118+
results: [
119+
{
120+
categories: {
121+
[Category.HATE]: false,
122+
},
123+
},
124+
],
125+
};
126+
});
127+
128+
// Create a context client that simulates a third-party provider
129+
// When moderation is called, it should raise a 404 error
130+
const contextCreateMock = vi.fn().mockRejectedValue({
131+
status: 404,
132+
message: '404 page not found',
133+
});
134+
135+
const OpenAI = (await import('openai')).default;
136+
const thirdPartyClient = new OpenAI({ apiKey: 'third-party-key', baseURL: 'https://localhost:8080/v1' });
137+
thirdPartyClient.moderations = {
138+
create: contextCreateMock,
139+
} as unknown as typeof thirdPartyClient.moderations;
140+
141+
const ctx = { guardrailLlm: thirdPartyClient };
142+
const cfg = ModerationConfig.parse({ categories: [Category.HATE] });
143+
const result = await moderationCheck(ctx, 'test text', cfg);
144+
145+
// Verify the fallback client was used (not the third-party one)
146+
expect(contextCreateMock).toHaveBeenCalled();
147+
expect(fallbackUsed).toBe(true);
148+
expect(result.tripwireTriggered).toBe(false);
149+
});
70150
});
71151

72152
describe('secret key guardrail', () => {

src/checks/moderation.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,6 @@ export const moderationCheck: CheckFn<ModerationContext, string, ModerationConfi
156156
try {
157157
resp = await callModerationAPI(client, data);
158158
} catch (error) {
159-
160159
// Moderation endpoint doesn't exist on this provider (e.g., third-party)
161160
// Fall back to the OpenAI client
162161
if (isNotFoundError(error)) {

0 commit comments

Comments
 (0)