Skip to content

Generate prompt for user based on their history/labels#232

Merged
elie222 merged 4 commits intomainfrom
generate-user-prompt
Sep 24, 2024
Merged

Generate prompt for user based on their history/labels#232
elie222 merged 4 commits intomainfrom
generate-user-prompt

Conversation

@elie222
Copy link
Owner

@elie222 elie222 commented Sep 24, 2024

Summary by CodeRabbit

  • New Features

    • Introduced an "AI Generate Prompt" button to generate rules based on recent email activity.
    • Added functionality to create email management rules using AI based on user data.
  • Bug Fixes

    • Improved button loading state for a better user experience during asynchronous actions.
  • Refactor

    • Streamlined button loading state representation by integrating a loading prop into button components, enhancing UI feedback.
  • Chores

    • Updated utility functions and imports for improved functionality and error handling.

@vercel
Copy link

vercel bot commented Sep 24, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
inbox-zero ✅ Ready (Inspect) Visit Preview Sep 24, 2024 6:07am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 24, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The changes introduce a new feature that enhances user interaction by adding a "loading" prop to various button components across multiple files. This prop replaces the previous conditional rendering of ButtonLoader components, simplifying the representation of loading states during asynchronous operations. Additionally, a new button labeled "AI Generate Prompt" is introduced in the RulesPromptForm component, enabling users to generate tailored rules prompts based on their recent email activity.

Changes

File Path Change Summary
apps/web/app/(app)/automation/RulesPrompt.tsx Added an "AI Generate Prompt" button in RulesPromptForm, integrating generateRulesPromptAction for asynchronous prompt generation.
apps/web/app/(app)/automation/BulkRunRules.tsx Updated Button to use a loading prop instead of conditional rendering for loading states.
apps/web/app/(app)/automation/Pending.tsx Simplified loading state management in PendingTable and ExecuteButtons components using loading prop.
apps/web/app/(app)/automation/create/page.tsx Modified button in AutomationSettingsPage to utilize a loading prop for submission state.
apps/web/app/(app)/automation/group/CreateGroupModal.tsx Added loading prop to buttons in CreateGroupModal, replacing previous loading indicators.
apps/web/app/(app)/automation/group/ViewGroup.tsx Changed disabled prop to loading prop in AddGroupItemForm button for better user feedback.
apps/web/app/(app)/bulk-unsubscribe/BulkActions.tsx Updated buttons for bulk actions to use loading prop, removing ButtonLoader conditional rendering.
apps/web/app/(app)/bulk-unsubscribe/common.tsx Added loading prop to ApproveButton, replacing the conditional loader.
apps/web/app/(app)/cold-email-blocker/ColdEmailList.tsx Replaced disabled prop with loading for buttons indicating rejection and marking cold emails.
apps/web/app/(landing)/components/page.tsx Modified ShadButton to set loading prop directly, simplifying loading state representation.
apps/web/app/(landing)/welcome/form.tsx Updated OnboardingForm buttons to use loading prop instead of disabled during submission.

Possibly related PRs

🐇 In a world of emails, so vast and wide,
A button now helps, with AI as our guide.
From labels and messages, rules we shall see,
Generating prompts, oh what fun it will be!
With a click and a toast, our inboxes will cheer,
For managing emails, the future is here! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    -- I pushed a fix in commit <commit_id>, please review it.
    -- Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    -- @coderabbitai generate unit testing code for this file.
    -- @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    -- @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    -- @coderabbitai read src/utils.ts and generate unit testing code.
    -- @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    -- @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@elie222 elie222 merged commit a1aa200 into main Sep 24, 2024
@elie222 elie222 deleted the generate-user-prompt branch September 24, 2024 06:02
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Outside diff range and nitpick comments (3)
apps/web/utils/mail.ts (1)

76-87: LGTM with suggestions for improvement

The changes to the emailToContent function look good overall. The addition of the optional options parameter with maxLength provides more flexibility in controlling the parsed email content length.

Here are a few suggestions to further improve the code:

  1. Consider adding a return type annotation to the function for better type safety:
export function emailToContent(
  email: {
    textHtml: string | null;
    textPlain: string | null;
    snippet: string | null;
  },
  options?: { maxLength: number }
): string {
  // ... function body ...
}
  1. It might be beneficial to add validation for the maxLength value to ensure it's a positive number:
const maxLength = options?.maxLength && options.maxLength > 0 ? options.maxLength : null;
const content =
  (email.textHtml && parseEmail(email.textHtml, false, maxLength)) ||
  email.textPlain ||
  email.snippet;

These changes will enhance the robustness and type safety of the function.

apps/web/app/(landing)/components/page.tsx (1)

64-66: Approve changes and suggest minor improvement for consistency

The modification to use the loading prop directly on the ShadButton component is a good improvement. It simplifies the code and is consistent with the Button component usage earlier in the file.

For better consistency across the demo, consider adding loading state examples for other button variants as well. This would provide a more comprehensive showcase of the component's capabilities. For example:

<ShadButton variant="default" loading>
  ShadButton Default Loading
</ShadButton>
<ShadButton variant="secondary" loading>
  ShadButton Secondary Loading
</ShadButton>
apps/web/app/(app)/automation/RulesPrompt.tsx (1)

184-215: LGTM: AI Generate Prompt button implemented correctly.

The new button and its associated functionality are well-implemented. The code follows React best practices, uses the existing toast notification system, and properly handles errors. The loading state is correctly managed, and the generated prompt is appropriately set to the form's state.

Consider extracting the onClick handler into a separate function for improved readability and maintainability. For example:

const handleGeneratePrompt = useCallback(async () => {
  toast.promise(
    async () => {
      setIsGenerating(true);
      const result = await generateRulesPromptAction();
      setIsGenerating(false);
      if (isActionError(result))
        throw new Error(result.error);
      if (!result)
        throw new Error("Unable to generate prompt");
      return result;
    },
    {
      loading: "Generating prompt...",
      success: (result) => {
        setValue("rulesPrompt", result.rulesPrompt);
        return "Prompt generated successfully!";
      },
      error: (err) => {
        return `Error generating prompt: ${err.message}`;
      },
    },
  );
}, [setValue]);

Then, you can simplify the button's onClick prop:

onClick={handleGeneratePrompt}

This change would improve code organization and make the component more readable.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 580e216 and f4429d8.

Files selected for processing (6)
  • apps/web/app/(app)/automation/RulesPrompt.tsx (3 hunks)
  • apps/web/app/(landing)/components/page.tsx (1 hunks)
  • apps/web/components/ui/button.tsx (3 hunks)
  • apps/web/utils/actions/ai-rule.ts (4 hunks)
  • apps/web/utils/ai/rule/generate-rules-prompt.ts (1 hunks)
  • apps/web/utils/mail.ts (1 hunks)
Additional context used
Biome
apps/web/utils/actions/ai-rule.ts

[error] 714-714: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)

apps/web/utils/ai/rule/generate-rules-prompt.ts

[error] 2-3: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the transpilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)

Additional comments not posted (10)
apps/web/utils/mail.ts (1)

76-87: Verify usage of emailToContent in the codebase

To ensure that the changes to emailToContent don't introduce any issues in the existing codebase, it's important to verify its usage. Please run the following script to check for all occurrences of emailToContent:

Review the output to ensure that all calls to emailToContent are compatible with the new function signature. If any calls need to be updated to include the new options parameter, please make the necessary changes.

apps/web/components/ui/button.tsx (5)

4-4: LGTM: Loader2 import added correctly

The import of Loader2 from 'lucide-react' is correctly added and necessary for the new loading state functionality.


36-38: LGTM: Loading variant added to buttonVariants

The addition of the 'loading' variant to buttonVariants is well-implemented:

  1. It applies appropriate styles for a disabled state when loading is true.
  2. The default value of false for loading ensures backwards compatibility.

These changes effectively support the new loading state functionality.

Also applies to: 43-43


52-52: LGTM: Loading prop added to ButtonProps interface

The addition of the optional loading prop to the ButtonProps interface is correct and well-typed. This allows users of the Button component to control the loading state, enhancing the component's flexibility.


56-67: LGTM: Loading prop correctly added to destructured props

The loading prop is correctly added to the destructured props with a default value of false. This ensures backwards compatibility and aligns with the interface definition. The placement among other props is appropriate.


Line range hint 1-94: Overall assessment: Well-implemented loading state with room for minor enhancement

The changes to the Button component effectively implement a loading state with good practices:

  1. The buttonVariants function and ButtonProps interface are correctly updated.
  2. The component handles the loading state appropriately, including visual feedback and disabling.
  3. The changes maintain backwards compatibility.

The suggestion to make children rendering optional during loading could further improve the component's flexibility. Consider implementing this enhancement in a future iteration.

Great work on this feature addition!

apps/web/utils/ai/rule/generate-rules-prompt.ts (1)

11-19: Function signature and type definitions look good

The function signature is well-defined with appropriate TypeScript types. The use of destructuring in the parameter object improves readability, and the return type is correctly specified.

apps/web/app/(app)/automation/RulesPrompt.tsx (3)

11-14: LGTM: Import statement updated correctly.

The addition of generateRulesPromptAction import is consistent with the new AI generation feature. The import statement is correctly structured and follows the existing pattern.


81-81: LGTM: New state variable added for AI generation.

The addition of the isGenerating state variable is appropriate for managing the loading state of the AI prompt generation process. The naming convention is consistent with the existing code style.


Line range hint 1-245: Overall implementation: Well-integrated and enhances functionality.

The new AI prompt generation feature has been seamlessly integrated into the existing RulesPromptForm component. The implementation:

  1. Maintains the existing component structure and functionality.
  2. Follows consistent coding patterns and styles.
  3. Enhances the user experience by providing an AI-generated prompt option.
  4. Properly handles loading states and error scenarios.

The changes demonstrate a good balance between adding new functionality and maintaining code quality and consistency.

Comment on lines +76 to 87
export function emailToContent(
email: {
textHtml: string | null;
textPlain: string | null;
snippet: string | null;
},
options?: { maxLength: number },
): string {
const content =
(email.textHtml && parseEmail(email.textHtml, false, null)) ||
(email.textHtml && parseEmail(email.textHtml, false, options?.maxLength)) ||
email.textPlain ||
email.snippet;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding documentation and ensuring consistency

While the changes provide more flexibility, it's important to ensure clarity and consistency in usage. Consider the following suggestions:

  1. Add a comment to document the default behavior and the new option:
/**
 * Converts an email object to a content string.
 * @param email - The email object containing textHtml, textPlain, and snippet.
 * @param options - Optional configuration object.
 * @param options.maxLength - Maximum length for the parsed content. If not provided, defaults to 2000 characters.
 * @returns The parsed and potentially truncated email content as a string.
 */
export function emailToContent(
  // ... function signature ...
) {
  // ... function body ...
}
  1. Ensure consistency across the codebase by reviewing other places where emailToContent is called. You may want to consider whether the default maxLength of 2000 characters (defined in the parseEmail function) is still appropriate for all use cases, or if it should be adjusted.

Comment on lines +72 to +86
className={cn(buttonVariants({ variant, size, loading, className }))}
ref={ref}
type={type}
disabled={loading || props.disabled}
{...props}
/>
>
{loading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
{children}
</>
) : (
children
)}
</Comp>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

LGTM with a minor suggestion: Button rendering updated for loading state

The changes to the Button component's rendering are well-implemented:

  1. The buttonVariants function correctly includes the loading state.
  2. The button is properly disabled when either loading or props.disabled is true.
  3. The conditional rendering shows a Loader2 icon during the loading state, which is good for UX.

However, I have a minor suggestion:

Consider making the rendering of children in the loading state optional. Some use cases might prefer to show only the loading indicator. You could achieve this by adding a new prop, e.g., showChildrenWhileLoading:

 export interface ButtonProps
   extends React.ButtonHTMLAttributes<HTMLButtonElement>,
     VariantProps<typeof buttonVariants> {
   asChild?: boolean;
   loading?: boolean;
+  showChildrenWhileLoading?: boolean;
 }

 const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
   ({
     className,
     variant,
     size,
     asChild = false,
     loading = false,
+    showChildrenWhileLoading = true,
     children,
     ...props
   },
   ref,
 ) => {
   // ... existing code ...
   return (
     <Comp
       className={cn(buttonVariants({ variant, size, loading, className }))}
       ref={ref}
       type={type}
       disabled={loading || props.disabled}
       {...props}
     >
       {loading ? (
         <>
           <Loader2 className="mr-2 h-4 w-4 animate-spin" />
-          {children}
+          {showChildrenWhileLoading && children}
         </>
       ) : (
         children
       )}
     </Comp>
   );
 },
);

This change would provide more flexibility for different use cases while maintaining the current behavior by default.

Committable suggestion was skipped due to low confidence.

Comment on lines +20 to +59
const emailSummary = lastSentEmails
.map((email, index) => `Email ${index + 1}:\n${email}\n`)
.join("\n");

const labelsList = userLabels.map((label) => `- ${label}`).join("\n");

const system =
"You are an AI assistant that helps people manage their emails by generating rules based on their email behavior and existing labels.";

const prompt = `
Analyze the user's email behavior and suggest general rules for managing their inbox effectively. Here's the context:

User Email: ${user.email}

Last 20 Sent Emails:
${emailSummary}

User's Labels:
${labelsList}

Generate a list of email management rules that would be broadly applicable for this user based on their email behavior and existing labels. The rules should be general enough to apply to various situations, not just specific recent emails. Include actions such as labeling, archiving, forwarding, replying, and drafting responses. Here are some examples of the format and complexity of rules you can create:

* Label newsletters as "Newsletter" and archive them
* If someone asks to schedule a meeting, send them your calendar link
* For cold emails or unsolicited pitches, draft a polite decline response
* Label emails related to financial matters as "Finance" and mark as important
* Forward emails about technical issues to the support team
* For emails from key clients or partners, label as "VIP" and keep in inbox

Focus on creating rules that will help the user organize their inbox more efficiently, save time, and automate responses where appropriate. Consider the following aspects:

1. Labeling and organizing emails by general categories (e.g., Work, Personal, Finance)
2. Handling common types of requests (e.g., meeting requests, support inquiries)
3. Automating responses for recurring scenarios
4. Forwarding specific types of emails to relevant team members
5. Prioritizing important or urgent emails
6. Dealing with newsletters, marketing emails, and potential spam

Your response should only include the list of general rules. Aim for 3-15 broadly applicable rules that would be useful for this user's email management.
`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider extracting the long prompt into a separate constant or file

The prompt construction logic is clear and well-structured. However, the prompt is quite long and might benefit from being extracted into a separate constant or file for better maintainability.

Consider creating a separate file, e.g., promptTemplates.ts, to store this and other prompt templates. This would improve code organization and make it easier to manage and update prompts in the future.

Example:

// promptTemplates.ts
export const GENERATE_RULES_PROMPT = `
Analyze the user's email behavior and suggest general rules for managing their inbox effectively. Here's the context:

User Email: {{userEmail}}

Last 20 Sent Emails:
{{emailSummary}}

User's Labels:
{{labelsList}}

// ... rest of the prompt ...
`;

// In generate-rules-prompt.ts
import { GENERATE_RULES_PROMPT } from './promptTemplates';

// ...

const prompt = GENERATE_RULES_PROMPT
  .replace('{{userEmail}}', user.email)
  .replace('{{emailSummary}}', emailSummary)
  .replace('{{labelsList}}', labelsList);

Comment on lines +61 to +73
const aiResponse = await chatCompletionTools({
userAi: user,
prompt,
system,
tools: {
generate_rules: {
description: "Generate a list of email management rules",
parameters,
},
},
userEmail: user.email,
label: "Generate rules prompt",
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for AI interaction

The AI interaction logic looks good, but it lacks error handling. Consider adding try-catch blocks to handle potential errors during the AI interaction.

Here's a suggested implementation:

try {
  const aiResponse = await chatCompletionTools({
    userAi: user,
    prompt,
    system,
    tools: {
      generate_rules: {
        description: "Generate a list of email management rules",
        parameters,
      },
    },
    userEmail: user.email,
    label: "Generate rules prompt",
  });

  const parsedRules = aiResponse.toolCalls[0].args as z.infer<typeof parameters>;
  return parsedRules.rules;
} catch (error) {
  console.error("Error generating rules:", error);
  throw new Error("Failed to generate email management rules");
}

Comment on lines +75 to +80
const parsedRules = aiResponse.toolCalls[0].args as z.infer<
typeof parameters
>;

return parsedRules.rules;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add validation for AI response and handle parsing errors

The current implementation assumes that the AI's response always matches the expected format and that the first tool call contains the rules. This might lead to runtime errors if the response is unexpected.

Consider adding validation and error handling:

const parsedResponse = parameters.safeParse(aiResponse.toolCalls[0]?.args);

if (!parsedResponse.success) {
  console.error("Invalid AI response:", parsedResponse.error);
  throw new Error("Failed to parse AI-generated rules");
}

return parsedResponse.data.rules;

This approach uses Zod's safeParse method to validate the response and provides better error handling.

Comment on lines +1 to +9
import { z } from "zod";
import { chatCompletionTools } from "@/utils/llms";
import { type UserAIFields } from "@/utils/llms/types";

const parameters = z.object({
rules: z
.array(z.string())
.describe("List of generated rules for email management"),
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using import type for type-only imports

To improve code quality and ensure that types are removed during transpilation, consider using import type for imports that are only used as types.

Apply this diff to update the import statements:

-import { z } from "zod";
+import type { z } from "zod";
 import { chatCompletionTools } from "@/utils/llms";
-import { type UserAIFields } from "@/utils/llms/types";
+import type { UserAIFields } from "@/utils/llms/types";

Committable suggestion was skipped due to low confidence.

Tools
Biome

[error] 2-3: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the transpilers and avoids loading unnecessary modules.
Safe fix: Use import type.

(lint/style/useImportType)

const userLabels = await getLabels(gmail);
const lastSentMessages = await Promise.all(
lastSent.messages?.map(async (message) => {
const gmailMessage = await getMessage(message.id!, gmail);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid using non-null assertion operator ! on message.id

Using the non-null assertion operator ! on message.id can lead to runtime errors if message.id is null or undefined. Consider adding a null check to ensure message.id is defined before using it.

Apply this diff to fix the issue:

- const gmailMessage = await getMessage(message.id!, gmail);
+ if (message.id) {
+   const gmailMessage = await getMessage(message.id, gmail);
+   return parseMessage(gmailMessage);
+ } else {
+   // Handle the case when message.id is undefined
+   // For example, log an error or skip processing this message
+   return null;
+ }

Committable suggestion was skipped due to low confidence.

Tools
Biome

[error] 714-714: Forbidden non-null assertion.

(lint/style/noNonNullAssertion)

This was referenced Oct 13, 2024
@coderabbitai coderabbitai bot mentioned this pull request Oct 30, 2024
This was referenced Nov 22, 2024
@coderabbitai coderabbitai bot mentioned this pull request Dec 5, 2024
@coderabbitai coderabbitai bot mentioned this pull request Aug 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments