@@ -164,6 +164,145 @@ describe("GeminiHandler", () => {
164164 } )
165165 } )
166166
167+ describe ( "grounding bypass for code generation" , ( ) => {
168+ it ( "should disable grounding when code generation context is detected" , async ( ) => {
169+ const codeHandler = new GeminiHandler ( {
170+ apiModelId : GEMINI_20_FLASH_THINKING_NAME ,
171+ geminiApiKey : "test-key" ,
172+ enableGrounding : true , // Grounding is enabled
173+ } )
174+
175+ // Mock the client's generateContentStream method
176+ const mockGenerateContentStream = vi . fn ( ) . mockResolvedValue ( {
177+ [ Symbol . asyncIterator ] : async function * ( ) {
178+ yield {
179+ text : "fruits = ['apple', 'banana']\nmyfruit = fruits[0]" ,
180+ usageMetadata : { promptTokenCount : 100 , candidatesTokenCount : 50 } ,
181+ }
182+ } ,
183+ } )
184+ codeHandler [ "client" ] . models . generateContentStream = mockGenerateContentStream
185+
186+ // Test message that includes code generation request
187+ const messages : Anthropic . Messages . MessageParam [ ] = [
188+ {
189+ role : "user" ,
190+ content : [
191+ {
192+ type : "text" ,
193+ text : "Create a python file that creates a list of 10 fruits and put the first one in the variable 'myfruit'" ,
194+ } ,
195+ ] ,
196+ } ,
197+ ]
198+
199+ const stream = codeHandler . createMessage ( "System prompt" , messages )
200+ const chunks = [ ]
201+ for await ( const chunk of stream ) {
202+ chunks . push ( chunk )
203+ }
204+
205+ // Verify that googleSearch was NOT added to tools (grounding disabled)
206+ const callArgs = mockGenerateContentStream . mock . calls [ 0 ] [ 0 ]
207+ const tools = callArgs . config . tools || [ ]
208+ const hasGrounding = tools . some ( ( tool : any ) => "googleSearch" in tool )
209+ expect ( hasGrounding ) . toBe ( false )
210+ } )
211+
212+ it ( "should enable grounding for non-code contexts when enableGrounding is true" , async ( ) => {
213+ const nonCodeHandler = new GeminiHandler ( {
214+ apiModelId : GEMINI_20_FLASH_THINKING_NAME ,
215+ geminiApiKey : "test-key" ,
216+ enableGrounding : true ,
217+ } )
218+
219+ // Mock the client's generateContentStream method
220+ const mockGenerateContentStream = vi . fn ( ) . mockResolvedValue ( {
221+ [ Symbol . asyncIterator ] : async function * ( ) {
222+ yield {
223+ text : "The weather today is sunny." ,
224+ usageMetadata : { promptTokenCount : 100 , candidatesTokenCount : 50 } ,
225+ }
226+ } ,
227+ } )
228+ nonCodeHandler [ "client" ] . models . generateContentStream = mockGenerateContentStream
229+
230+ // Test message without code generation context
231+ const messages : Anthropic . Messages . MessageParam [ ] = [
232+ {
233+ role : "user" ,
234+ content : [
235+ {
236+ type : "text" ,
237+ text : "What's the weather like today?" ,
238+ } ,
239+ ] ,
240+ } ,
241+ ]
242+
243+ const stream = nonCodeHandler . createMessage ( "System prompt" , messages )
244+ const chunks = [ ]
245+ for await ( const chunk of stream ) {
246+ chunks . push ( chunk )
247+ }
248+
249+ // Verify that googleSearch WAS added to tools (grounding enabled)
250+ const callArgs = mockGenerateContentStream . mock . calls [ 0 ] [ 0 ]
251+ const tools = callArgs . config . tools || [ ]
252+ const hasGrounding = tools . some ( ( tool : any ) => "googleSearch" in tool )
253+ expect ( hasGrounding ) . toBe ( true )
254+ } )
255+
256+ it ( "should correctly identify various code generation patterns" , async ( ) => {
257+ const handler = new GeminiHandler ( {
258+ apiModelId : GEMINI_20_FLASH_THINKING_NAME ,
259+ geminiApiKey : "test-key" ,
260+ } )
261+
262+ // Test various code generation patterns
263+ const codePatterns = [
264+ "Create a python file with a list" ,
265+ "Write a javascript function" ,
266+ "Generate code snippet" ,
267+ "Implement a class method" ,
268+ "def my_function():" ,
269+ "function getData() {" ,
270+ "fruits[0] = 'apple'" ,
271+ "array[5]" ,
272+ ]
273+
274+ for ( const pattern of codePatterns ) {
275+ const messages : Anthropic . Messages . MessageParam [ ] = [
276+ {
277+ role : "user" ,
278+ content : [ { type : "text" , text : pattern } ] ,
279+ } ,
280+ ]
281+ const result = handler [ "isCodeGenerationContext" ] ( messages )
282+ expect ( result ) . toBe ( true )
283+ }
284+
285+ // Test non-code patterns
286+ const nonCodePatterns = [
287+ "What's the weather?" ,
288+ "Explain quantum physics" ,
289+ "Tell me a story" ,
290+ "How do I cook pasta?" ,
291+ ]
292+
293+ for ( const pattern of nonCodePatterns ) {
294+ const messages : Anthropic . Messages . MessageParam [ ] = [
295+ {
296+ role : "user" ,
297+ content : [ { type : "text" , text : pattern } ] ,
298+ } ,
299+ ]
300+ const result = handler [ "isCodeGenerationContext" ] ( messages )
301+ expect ( result ) . toBe ( false )
302+ }
303+ } )
304+ } )
305+
167306 describe ( "calculateCost" , ( ) => {
168307 // Mock ModelInfo based on gemini-1.5-flash-latest pricing (per 1M tokens)
169308 // Removed 'id' and 'name' as they are not part of ModelInfo type directly
0 commit comments