1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
- import { afterEach, beforeEach, describe, expect, it, jest } from '@jest/globals';
- import RedteamGoatProvider from '../../../src/redteam/providers/goat';
- import { getRemoteGenerationUrl } from '../../../src/redteam/remoteGeneration';
- import type {
- ApiProvider,
- AtomicTestCase,
- CallApiContextParams,
- Prompt,
- ProviderResponse,
- } from '../../../src/types';
- describe('RedteamGoatProvider', () => {
- let mockFetch: jest.Mock;
- beforeEach(() => {
- mockFetch = jest.fn().mockImplementation(async () => ({
- json: async () => ({
- message: { role: 'assistant', content: 'test response' },
- }),
- ok: true,
- }));
- global.fetch = mockFetch as unknown as typeof fetch;
- });
- afterEach(() => {
- jest.clearAllMocks();
- });
- it('should initialize with required config', () => {
- const provider = new RedteamGoatProvider({
- injectVar: 'goal',
- maxTurns: 3,
- });
- expect(provider.id()).toBe('promptfoo:redteam:goat');
- });
- it('should throw error if injectVar is missing', () => {
- expect(() => {
- new RedteamGoatProvider({});
- }).toThrow('Expected injectVar to be set');
- });
- it('should make correct API calls with message history', async () => {
- const provider = new RedteamGoatProvider({
- injectVar: 'goal',
- maxTurns: 2,
- });
- const targetProvider: ApiProvider = {
- id: () => 'test-provider',
- callApi: async () =>
- Promise.resolve({
- output: 'target response',
- tokenUsage: {},
- }) as ProviderResponse,
- };
- const prompt: Prompt = {
- raw: 'test prompt',
- label: 'test',
- };
- const context: CallApiContextParams = {
- originalProvider: targetProvider,
- vars: { goal: 'test goal' },
- prompt,
- };
- const result = await provider.callApi('test prompt', context);
- expect(result.metadata?.stopReason).toBe('Max turns reached');
- expect(mockFetch).toHaveBeenCalledWith(
- getRemoteGenerationUrl(),
- expect.objectContaining({
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: expect.any(String),
- }),
- );
- });
- it('should handle grader integration and stop early on failure', async () => {
- const provider = new RedteamGoatProvider({
- injectVar: 'goal',
- maxTurns: 3,
- });
- const targetProvider: ApiProvider = {
- id: () => 'test-provider',
- callApi: async () =>
- Promise.resolve({
- output: 'target response',
- tokenUsage: { total: 10, prompt: 5, completion: 5 },
- }) as ProviderResponse,
- };
- const context: CallApiContextParams = {
- originalProvider: targetProvider,
- vars: { goal: 'test goal' },
- prompt: { raw: 'test prompt', label: 'test' },
- test: {
- vars: {},
- assert: [
- {
- type: 'contains',
- value: 'expected content',
- },
- ],
- } as AtomicTestCase,
- };
- jest.mock('../../../src/redteam/graders', () => ({
- getGraderById: () => ({
- getResult: async () => ({
- grade: {
- pass: false,
- tokensUsed: { total: 5, prompt: 2, completion: 3 },
- },
- }),
- }),
- }));
- const result = await provider.callApi('test prompt', context);
- expect(result.metadata?.stopReason).toBe('Grader failed');
- expect(result.tokenUsage?.total).toBeGreaterThan(0);
- });
- it('should stringify non-string target provider responses', async () => {
- const provider = new RedteamGoatProvider({
- injectVar: 'goal',
- maxTurns: 1,
- });
- const objectResponse = { foo: 'bar', baz: 123 };
- const targetProvider: ApiProvider = {
- id: () => 'test-provider',
- callApi: async () =>
- Promise.resolve({
- output: objectResponse,
- tokenUsage: {},
- }) as ProviderResponse,
- };
- const prompt: Prompt = {
- raw: 'test prompt',
- label: 'test',
- };
- const context: CallApiContextParams = {
- originalProvider: targetProvider,
- vars: { goal: 'test goal' },
- prompt,
- };
- const result = await provider.callApi('test prompt', context);
- const messages = result.metadata?.messages;
- expect(messages[messages.length - 1].content).toBe(JSON.stringify(objectResponse));
- });
- });
|