|
| 1 | +import { CoreMessage } from 'ai'; |
| 2 | +import { z } from 'zod'; |
| 3 | +import { promptAiSdkStructured } from './vercel-ai-sdk/ai-sdk'; |
| 4 | +import { models } from 'common/constants'; |
| 5 | +import { getCoreMessagesSubset } from '../util/messages'; |
| 6 | +import { logger } from '../util/logger'; |
| 7 | + |
| 8 | +const loopCheckSchema = z.object({ |
| 9 | + is_loop: z.boolean().describe('Whether the assistant is in a non-productive loop.'), |
| 10 | +}); |
| 11 | + |
| 12 | +export async function checkForUnproductiveLoop( |
| 13 | + messages: CoreMessage[], |
| 14 | + options: { |
| 15 | + clientSessionId: string; |
| 16 | + fingerprintId: string; |
| 17 | + userInputId: string; |
| 18 | + userId: string | undefined; |
| 19 | + } |
| 20 | +): Promise<boolean> { |
| 21 | + const { clientSessionId, fingerprintId, userInputId, userId } = options; |
| 22 | + |
| 23 | + const relevantMessages = getCoreMessagesSubset(messages, 0).slice(-6); |
| 24 | + |
| 25 | + if (relevantMessages.length < 4) { |
| 26 | + return false; |
| 27 | + } |
| 28 | + |
| 29 | + const prompt = ` |
| 30 | +The following is a sequence of messages between a user and an AI assistant. |
| 31 | +Your task is to determine if the assistant is stuck in a non-productive loop. |
| 32 | +
|
| 33 | +A non-productive loop is defined as the assistant repeatedly taking similar actions (e.g., using the same tool or running the same terminal command) across multiple turns without making meaningful progress toward resolving the user's request. The assistant might be making small changes, but they don't fix the underlying issue, leading to a cycle of similar errors and attempts. |
| 34 | +
|
| 35 | +Analyze the message history and determine if a non-productive loop is occurring. |
| 36 | +
|
| 37 | +Respond with a JSON object matching the following schema: |
| 38 | +{ |
| 39 | + "is_loop": boolean, // true if a loop is detected, false otherwise. |
| 40 | +} |
| 41 | +`; |
| 42 | + |
| 43 | + try { |
| 44 | + const result = await promptAiSdkStructured({ |
| 45 | + messages: [ |
| 46 | + ...relevantMessages, |
| 47 | + { role: 'user', content: prompt }, |
| 48 | + ], |
| 49 | + schema: loopCheckSchema, |
| 50 | + model: models.gemini2_5_flash, |
| 51 | + clientSessionId, |
| 52 | + fingerprintId, |
| 53 | + userInputId, |
| 54 | + userId, |
| 55 | + temperature: 0, |
| 56 | + }); |
| 57 | + |
| 58 | + logger.debug({ result }, 'Unproductive loop check result'); |
| 59 | + return result.is_loop; |
| 60 | + } catch (error) { |
| 61 | + logger.error({ error }, 'Error checking for unproductive loop'); |
| 62 | + return false; |
| 63 | + } |
| 64 | +} |
0 commit comments