Skip to content

Commit 6888a6b

Browse files
authored
More robust handling of checking guardrailLlm client type (#32)
* Properly pass back execution errors * Remove unneeded OAI check * Extract error helper * Copilot comments
1 parent 50df54b commit 6888a6b

File tree

2 files changed

+59
-21
lines changed

2 files changed

+59
-21
lines changed

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,23 @@ describe('moderation guardrail', () => {
6565
const result = await moderationCheck({}, 'safe text', ModerationConfig.parse({}));
6666

6767
expect(result.tripwireTriggered).toBe(false);
68-
expect(result.info?.error).toBe('Moderation API call failed');
68+
expect(result.executionFailed).toBe(true);
69+
expect(result.originalException).toBeDefined();
70+
expect(result.info?.error).toContain('network down');
71+
});
72+
73+
it('returns executionFailed for API key errors to support raiseGuardrailErrors', async () => {
74+
const apiKeyError = new Error(
75+
'Incorrect API key provided: sk-invalid. You can find your API key at https://platform.openai.com/account/api-keys.'
76+
);
77+
createMock.mockRejectedValue(apiKeyError);
78+
79+
const result = await moderationCheck({}, 'test text', ModerationConfig.parse({}));
80+
81+
expect(result.tripwireTriggered).toBe(false);
82+
expect(result.executionFailed).toBe(true);
83+
expect(result.originalException).toBe(apiKeyError);
84+
expect(result.info?.error).toContain('Incorrect API key');
6985
});
7086

7187
it('uses context client when available', async () => {

src/checks/moderation.ts

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,17 @@ function isNotFoundError(error: unknown): boolean {
8989
return !!(error && typeof error === 'object' && 'status' in error && error.status === 404);
9090
}
9191

92+
/**
93+
* Ensure a value is an Error instance.
94+
* Converts non-Error values to Error instances with a string representation.
95+
*
96+
* @param error The error value to convert
97+
* @returns An Error instance
98+
*/
99+
function ensureError(error: unknown): Error {
100+
return error instanceof Error ? error : new Error(String(error));
101+
}
102+
92103
/**
93104
* Call the OpenAI moderation API.
94105
*
@@ -144,8 +155,9 @@ export const moderationCheck: CheckFn<ModerationContext, string, ModerationConfi
144155
if (ctx) {
145156
const contextObj = ctx as Record<string, unknown>;
146157
const candidate = contextObj.guardrailLlm;
147-
if (candidate && candidate instanceof OpenAI) {
148-
client = candidate;
158+
// Just use whatever is provided, let the try-catch handle validation
159+
if (candidate) {
160+
client = candidate as OpenAI;
149161
}
150162
}
151163

@@ -162,25 +174,32 @@ export const moderationCheck: CheckFn<ModerationContext, string, ModerationConfi
162174
try {
163175
resp = await callModerationAPI(new OpenAI(), data);
164176
} catch (fallbackError) {
165-
// If fallback fails, provide a helpful error message
166-
const errorMessage = fallbackError instanceof Error
167-
? fallbackError.message
168-
: String(fallbackError);
169-
170-
// Check if it's an API key error
171-
if (errorMessage.includes('api_key') || errorMessage.includes('OPENAI_API_KEY')) {
172-
return {
173-
tripwireTriggered: false,
174-
info: {
175-
checked_text: data,
176-
error: 'Moderation API requires OpenAI API key. Set OPENAI_API_KEY environment variable or pass a client with valid credentials.',
177-
},
178-
};
179-
}
180-
throw fallbackError;
177+
// If fallback fails, return execution failure
178+
// This allows runGuardrails to handle based on raiseGuardrailErrors flag
179+
const errorObj = ensureError(fallbackError);
180+
return {
181+
tripwireTriggered: false,
182+
executionFailed: true,
183+
originalException: errorObj,
184+
info: {
185+
checked_text: data,
186+
error: errorObj.message,
187+
},
188+
};
181189
}
182190
} else {
183-
throw error;
191+
// Non-404 error from context client - return execution failure
192+
// This allows runGuardrails to handle based on raiseGuardrailErrors flag
193+
const errorObj = ensureError(error);
194+
return {
195+
tripwireTriggered: false,
196+
executionFailed: true,
197+
originalException: errorObj,
198+
info: {
199+
checked_text: data,
200+
error: errorObj.message,
201+
},
202+
};
184203
}
185204
}
186205
} else {
@@ -230,11 +249,14 @@ export const moderationCheck: CheckFn<ModerationContext, string, ModerationConfi
230249
};
231250
} catch (error) {
232251
console.warn('AI-based moderation failed:', error);
252+
const errorObj = ensureError(error);
233253
return {
234254
tripwireTriggered: false,
255+
executionFailed: true,
256+
originalException: errorObj,
235257
info: {
236258
checked_text: data,
237-
error: 'Moderation API call failed',
259+
error: errorObj.message,
238260
},
239261
};
240262
}

0 commit comments

Comments
 (0)