Skip to content

Commit ad56701

Browse files
authored
fix: target URL filter (#4735)
* fix: add target_url mapping to request_response_rmt in filters * test: target url filter
1 parent a534732 commit ad56701

File tree

2 files changed

+235
-0
lines changed

2 files changed

+235
-0
lines changed
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
import {
2+
buildFilterClickHouse,
3+
} from '../../filters/filters';
4+
import {
5+
FilterLeaf,
6+
} from '../../filters/filterDefs';
7+
8+
describe('Target URL Filter', () => {
9+
describe('buildFilterClickHouse with target_url', () => {
10+
test('should build target_url equals filter correctly', () => {
11+
const filter: FilterLeaf = {
12+
request_response_rmt: {
13+
target_url: { equals: 'https://api.openai.com/v1/chat/completions' }
14+
}
15+
};
16+
17+
const result = buildFilterClickHouse({ filter, argsAcc: [] });
18+
19+
expect(result.filter).toBe('request_response_rmt.target_url = {val_0 : String}');
20+
expect(result.argsAcc).toEqual(['https://api.openai.com/v1/chat/completions']);
21+
});
22+
23+
test('should build target_url like filter correctly', () => {
24+
const filter: FilterLeaf = {
25+
request_response_rmt: {
26+
target_url: { like: '%openai.com%' }
27+
}
28+
};
29+
30+
const result = buildFilterClickHouse({ filter, argsAcc: [] });
31+
32+
expect(result.filter).toBe('request_response_rmt.target_url LIKE {val_0 : String}');
33+
expect(result.argsAcc).toEqual(['%openai.com%']);
34+
});
35+
36+
test('should build target_url ilike filter correctly', () => {
37+
const filter: FilterLeaf = {
38+
request_response_rmt: {
39+
target_url: { ilike: '%OPENAI.COM%' }
40+
}
41+
};
42+
43+
const result = buildFilterClickHouse({ filter, argsAcc: [] });
44+
45+
expect(result.filter).toBe('request_response_rmt.target_url ILIKE {val_0 : String}');
46+
expect(result.argsAcc).toEqual(['%OPENAI.COM%']);
47+
});
48+
49+
test('should build target_url contains filter correctly', () => {
50+
const filter: FilterLeaf = {
51+
request_response_rmt: {
52+
target_url: { contains: 'anthropic' }
53+
}
54+
};
55+
56+
const result = buildFilterClickHouse({ filter, argsAcc: [] });
57+
58+
expect(result.filter).toBe("request_response_rmt.target_url ILIKE '%' || {val_0 : String}::text || '%'");
59+
expect(result.argsAcc).toEqual(['anthropic']);
60+
});
61+
62+
test('should build target_url not-equals filter correctly', () => {
63+
const filter: FilterLeaf = {
64+
request_response_rmt: {
65+
target_url: { 'not-equals': 'https://api.openai.com/v1/embeddings' }
66+
}
67+
};
68+
69+
const result = buildFilterClickHouse({ filter, argsAcc: [] });
70+
71+
expect(result.filter).toBe('request_response_rmt.target_url != {val_0 : String}');
72+
expect(result.argsAcc).toEqual(['https://api.openai.com/v1/embeddings']);
73+
});
74+
75+
test('should handle target_url with null value', () => {
76+
const filter: FilterLeaf = {
77+
request_response_rmt: {
78+
target_url: { equals: 'null' }
79+
}
80+
};
81+
82+
const result = buildFilterClickHouse({ filter, argsAcc: [] });
83+
84+
expect(result.filter).toBe('request_response_rmt.target_url is null');
85+
expect(result.argsAcc).toEqual([]);
86+
});
87+
88+
test('should work with target_url alongside other basic fields', () => {
89+
// Test that target_url doesn't break when used with a simple status filter
90+
const simpleFilter: FilterLeaf = {
91+
request_response_rmt: {
92+
target_url: { equals: 'https://api.openai.com/v1/chat/completions' }
93+
}
94+
};
95+
96+
const result = buildFilterClickHouse({ filter: simpleFilter, argsAcc: [] });
97+
98+
// Should successfully build target_url filter
99+
expect(result.argsAcc).toEqual(['https://api.openai.com/v1/chat/completions']);
100+
expect(result.filter).toBe('request_response_rmt.target_url = {val_0 : String}');
101+
});
102+
});
103+
104+
describe('Target URL Filter Mapping Verification', () => {
105+
test('should verify target_url mapping exists in request_response_rmt', () => {
106+
// This test verifies that our fix is working by checking that target_url
107+
// filters can be built without throwing errors
108+
const filter: FilterLeaf = {
109+
request_response_rmt: {
110+
target_url: { equals: 'https://api.anthropic.com/v1/messages' }
111+
}
112+
};
113+
114+
// This should not throw an error now that target_url mapping is added
115+
expect(() => {
116+
buildFilterClickHouse({ filter, argsAcc: [] });
117+
}).not.toThrow();
118+
});
119+
120+
test('should handle target_url with different URL formats', () => {
121+
const testCases = [
122+
'https://api.openai.com/v1/chat/completions',
123+
'https://api.anthropic.com/v1/messages',
124+
'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent',
125+
'https://api.cohere.ai/v1/generate',
126+
'http://localhost:3000/api/proxy',
127+
'https://api.mistral.ai/v1/chat/completions',
128+
'wss://api.openai.com/v1/realtime',
129+
];
130+
131+
testCases.forEach((url) => {
132+
const filter: FilterLeaf = {
133+
request_response_rmt: {
134+
target_url: { equals: url }
135+
}
136+
};
137+
138+
const result = buildFilterClickHouse({ filter, argsAcc: [] });
139+
140+
expect(result.filter).toBe('request_response_rmt.target_url = {val_0 : String}');
141+
expect(result.argsAcc).toEqual([url]);
142+
});
143+
});
144+
145+
test('should handle target_url with special characters in URL', () => {
146+
const urlWithSpecialChars = 'https://api.example.com/v1/test?param=value&other=test%20string';
147+
const filter: FilterLeaf = {
148+
request_response_rmt: {
149+
target_url: { equals: urlWithSpecialChars }
150+
}
151+
};
152+
153+
const result = buildFilterClickHouse({ filter, argsAcc: [] });
154+
155+
expect(result.filter).toBe('request_response_rmt.target_url = {val_0 : String}');
156+
expect(result.argsAcc).toEqual([urlWithSpecialChars]);
157+
});
158+
});
159+
160+
describe('Target URL Filter Edge Cases', () => {
161+
test('should handle empty target_url value', () => {
162+
const filter: FilterLeaf = {
163+
request_response_rmt: {
164+
target_url: { equals: '' }
165+
}
166+
};
167+
168+
const result = buildFilterClickHouse({ filter, argsAcc: [] });
169+
170+
expect(result.filter).toBe('request_response_rmt.target_url = {val_0 : String}');
171+
expect(result.argsAcc).toEqual(['']);
172+
});
173+
174+
test('should handle very long target_url values', () => {
175+
const longUrl = 'https://api.example.com/v1/very/long/path/that/goes/on/and/on/' + 'segment/'.repeat(50) + '?param=value';
176+
const filter: FilterLeaf = {
177+
request_response_rmt: {
178+
target_url: { equals: longUrl }
179+
}
180+
};
181+
182+
const result = buildFilterClickHouse({ filter, argsAcc: [] });
183+
184+
expect(result.filter).toBe('request_response_rmt.target_url = {val_0 : String}');
185+
expect(result.argsAcc).toEqual([longUrl]);
186+
});
187+
188+
test('should handle target_url with unicode characters', () => {
189+
const unicodeUrl = 'https://api.example.com/v1/测试/пример/🚀?param=тест';
190+
const filter: FilterLeaf = {
191+
request_response_rmt: {
192+
target_url: { equals: unicodeUrl }
193+
}
194+
};
195+
196+
const result = buildFilterClickHouse({ filter, argsAcc: [] });
197+
198+
expect(result.filter).toBe('request_response_rmt.target_url = {val_0 : String}');
199+
expect(result.argsAcc).toEqual([unicodeUrl]);
200+
});
201+
});
202+
203+
describe('Target URL Filter Integration', () => {
204+
test('should work with existing argument arrays', () => {
205+
const filter: FilterLeaf = {
206+
request_response_rmt: {
207+
target_url: { equals: 'https://api.openai.com/v1/chat/completions' }
208+
}
209+
};
210+
211+
const existingArgs = ['existing_value_1', 'existing_value_2'];
212+
const result = buildFilterClickHouse({ filter, argsAcc: [...existingArgs] });
213+
214+
expect(result.filter).toBe('request_response_rmt.target_url = {val_2 : String}');
215+
expect(result.argsAcc).toEqual([...existingArgs, 'https://api.openai.com/v1/chat/completions']);
216+
});
217+
218+
test('should demonstrate the fix prevents previous error', () => {
219+
// Before the fix, this would cause an error because target_url was not mapped
220+
const filter: FilterLeaf = {
221+
request_response_rmt: {
222+
target_url: { contains: 'anthropic' }
223+
}
224+
};
225+
226+
// This should not throw an error now that target_url mapping is fixed
227+
expect(() => {
228+
const result = buildFilterClickHouse({ filter, argsAcc: [] });
229+
expect(result.filter).toContain("request_response_rmt.target_url ILIKE '%' || {val_0 : String}::text || '%'");
230+
expect(result.argsAcc).toEqual(['anthropic']);
231+
}).not.toThrow();
232+
});
233+
});
234+
});

packages/filters/filters.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ const whereKeyMappings: KeyMappings = {
277277
prompt_version: "request_response_rmt.prompt_version",
278278
request_referrer: "request_response_rmt.request_referrer",
279279
is_passthrough_billing: "request_response_rmt.is_passthrough_billing",
280+
target_url: "request_response_rmt.target_url",
280281
})(filter, placeValueSafely);
281282
},
282283
users_view: easyKeyMappings<"users_view">({}),

0 commit comments

Comments
 (0)