diff --git a/apps/web/app/api/google/webhook/process-history-item.test.ts b/apps/web/app/api/google/webhook/process-history-item.test.ts index 2ff6c732e7..97f812e70c 100644 --- a/apps/web/app/api/google/webhook/process-history-item.test.ts +++ b/apps/web/app/api/google/webhook/process-history-item.test.ts @@ -218,6 +218,7 @@ describe("processHistoryItem", () => { expect(runColdEmailBlockerWithProvider).toHaveBeenCalledWith({ email: expect.objectContaining({ from: "sender@example.com", + to: "", subject: "Test Email", content: expect.any(String), id: "123", @@ -226,6 +227,7 @@ describe("processHistoryItem", () => { }), provider: expect.any(Object), emailAccount: options.emailAccount, + modelType: "default", }); }); @@ -282,6 +284,7 @@ describe("processHistoryItem", () => { expect(runColdEmailBlockerWithProvider).toHaveBeenCalledWith({ email: expect.objectContaining({ from: "sender@example.com", + to: "", subject: "Test Email", content: expect.any(String), id: "123", @@ -290,6 +293,7 @@ describe("processHistoryItem", () => { }), provider: expect.any(Object), emailAccount: options.emailAccount, + modelType: "default", }); // Verify that cold email is added to digest @@ -381,6 +385,7 @@ describe("processHistoryItem", () => { expect(runColdEmailBlockerWithProvider).toHaveBeenCalledWith({ email: expect.objectContaining({ from: "sender@example.com", + to: "", subject: "Test Email", content: expect.any(String), id: "456", @@ -389,6 +394,7 @@ describe("processHistoryItem", () => { }), provider: expect.any(Object), emailAccount: options.emailAccount, + modelType: "default", }); // Verify that the second email from known cold emailer is added to digest diff --git a/apps/web/app/api/google/webhook/process-history-item.ts b/apps/web/app/api/google/webhook/process-history-item.ts index a9597a06f8..c1db10c997 100644 --- a/apps/web/app/api/google/webhook/process-history-item.ts +++ b/apps/web/app/api/google/webhook/process-history-item.ts @@ -164,6 +164,7 @@ export async function processHistoryItem( }, provider, emailAccount, + modelType: "default", }); if (response.isColdEmail) { @@ -206,6 +207,7 @@ export async function processHistoryItem( rules, emailAccount, isTest: false, + modelType: "default", }); } } catch (error: unknown) { diff --git a/apps/web/app/api/outlook/webhook/process-history-item.ts b/apps/web/app/api/outlook/webhook/process-history-item.ts index 7d62d524a4..fe88da9f7e 100644 --- a/apps/web/app/api/outlook/webhook/process-history-item.ts +++ b/apps/web/app/api/outlook/webhook/process-history-item.ts @@ -185,6 +185,7 @@ export async function processHistoryItem( }, provider: emailProvider, emailAccount, + modelType: "default", }); if (response.isColdEmail) { @@ -236,6 +237,7 @@ export async function processHistoryItem( rules, emailAccount, isTest: false, + modelType: "default", }); } } catch (error) { diff --git a/apps/web/utils/actions/ai-rule.ts b/apps/web/utils/actions/ai-rule.ts index b0684396b9..68197da4ab 100644 --- a/apps/web/utils/actions/ai-rule.ts +++ b/apps/web/utils/actions/ai-rule.ts @@ -101,6 +101,7 @@ export const runRulesAction = actionClient message, rules, emailAccount, + modelType: "chat", }); return result; @@ -152,6 +153,7 @@ export const testAiCustomContentAction = actionClient }, rules, emailAccount, + modelType: "chat", }); return result; diff --git a/apps/web/utils/actions/cold-email.ts b/apps/web/utils/actions/cold-email.ts index 1c0174271d..6bbb961867 100644 --- a/apps/web/utils/actions/cold-email.ts +++ b/apps/web/utils/actions/cold-email.ts @@ -153,6 +153,7 @@ export const testColdEmailAction = actionClient }, emailAccount, provider: emailProvider, + modelType: "chat", }); return response; diff --git a/apps/web/utils/ai/choose-rule/ai-choose-args.ts b/apps/web/utils/ai/choose-rule/ai-choose-args.ts index e0265e705f..6d02c8a375 100644 --- a/apps/web/utils/ai/choose-rule/ai-choose-args.ts +++ b/apps/web/utils/ai/choose-rule/ai-choose-args.ts @@ -10,6 +10,7 @@ import { createScopedLogger } from "@/utils/logger"; import type { EmailAccountWithAI } from "@/utils/llms/types"; import type { EmailForLLM, RuleWithActions } from "@/utils/types"; import type { ActionType } from "@prisma/client"; +import type { ModelType } from "@/utils/llms/model"; /** * AI Argument Generator for Email Actions @@ -43,6 +44,7 @@ export async function aiGenerateArgs({ emailAccount, selectedRule, parameters, + modelType, }: { email: EmailForLLM; emailAccount: EmailAccountWithAI; @@ -54,6 +56,7 @@ export async function aiGenerateArgs({ Record>> >; }[]; + modelType: ModelType; }) { const loggerOptions = { email: emailAccount.email, @@ -79,6 +82,7 @@ export async function aiGenerateArgs({ () => chatCompletionObject({ userAi: emailAccount.user, + modelType, prompt, system, schemaName: "Apply rule", diff --git a/apps/web/utils/ai/choose-rule/ai-choose-rule.ts b/apps/web/utils/ai/choose-rule/ai-choose-rule.ts index 8b3029561a..9cb05ac28f 100644 --- a/apps/web/utils/ai/choose-rule/ai-choose-rule.ts +++ b/apps/web/utils/ai/choose-rule/ai-choose-rule.ts @@ -4,6 +4,7 @@ import { chatCompletionObject } from "@/utils/llms"; import { stringifyEmail } from "@/utils/stringify-email"; import type { EmailForLLM } from "@/utils/types"; import { createScopedLogger } from "@/utils/logger"; +import type { ModelType } from "@/utils/llms/model"; // import { Braintrust } from "@/utils/braintrust"; const logger = createScopedLogger("ai-choose-rule"); @@ -14,10 +15,11 @@ type GetAiResponseOptions = { email: EmailForLLM; emailAccount: EmailAccountWithAI; rules: { name: string; instructions: string }[]; + modelType?: ModelType; }; async function getAiResponse(options: GetAiResponseOptions) { - const { email, emailAccount, rules } = options; + const { email, emailAccount, rules, modelType = "default" } = options; const emailSection = stringifyEmail(email, 500); @@ -80,6 +82,7 @@ ${emailSection} const aiResponse = await chatCompletionObject({ userAi: emailAccount.user, + modelType, messages: [ { role: "system", @@ -133,10 +136,12 @@ export async function aiChooseRule< email, rules, emailAccount, + modelType, }: { email: EmailForLLM; rules: T[]; emailAccount: EmailAccountWithAI; + modelType?: ModelType; }) { if (!rules.length) return { reason: "No rules" }; @@ -144,6 +149,7 @@ export async function aiChooseRule< email, rules, emailAccount, + modelType, }); if (aiResponse.noMatchFound) diff --git a/apps/web/utils/ai/choose-rule/choose-args.ts b/apps/web/utils/ai/choose-rule/choose-args.ts index 1e7142edef..3002cb5a74 100644 --- a/apps/web/utils/ai/choose-rule/choose-args.ts +++ b/apps/web/utils/ai/choose-rule/choose-args.ts @@ -1,5 +1,6 @@ import { z } from "zod"; import type { EmailAccountWithAI } from "@/utils/llms/types"; +import type { ModelType } from "@/utils/llms/model"; import { ActionType, type Action } from "@prisma/client"; import { type RuleWithActions, @@ -27,11 +28,13 @@ export async function getActionItemsWithAiArgs({ emailAccount, selectedRule, client, + modelType, }: { message: ParsedMessage; emailAccount: EmailAccountWithAI; selectedRule: RuleWithActions; client: EmailProvider; + modelType: ModelType; }): Promise { // Draft content is handled via its own AI call // We provide a lot more context to the AI to draft the content @@ -64,6 +67,7 @@ export async function getActionItemsWithAiArgs({ emailAccount, selectedRule, parameters, + modelType, }); return combineActionsWithAiArgs(selectedRule.actions, result, draft); diff --git a/apps/web/utils/ai/choose-rule/match-rules.ts b/apps/web/utils/ai/choose-rule/match-rules.ts index e93e4ea74a..0eb87b1350 100644 --- a/apps/web/utils/ai/choose-rule/match-rules.ts +++ b/apps/web/utils/ai/choose-rule/match-rules.ts @@ -27,6 +27,7 @@ import { extractEmailAddress } from "@/utils/email"; import { hasIcsAttachment } from "@/utils/parse/calender-event"; import { checkSenderReplyHistory } from "@/utils/reply-tracker/check-sender-reply-history"; import type { EmailProvider } from "@/utils/email/provider"; +import type { ModelType } from "@/utils/llms/model"; const logger = createScopedLogger("match-rules"); @@ -203,17 +204,20 @@ export async function findMatchingRule({ message, emailAccount, client, + modelType, }: { rules: RuleWithActionsAndCategories[]; message: ParsedMessage; emailAccount: EmailAccountWithAI; client: EmailProvider; + modelType: ModelType; }) { const result = await findMatchingRuleWithReasons( rules, message, emailAccount, client, + modelType, ); return { ...result, @@ -226,6 +230,7 @@ async function findMatchingRuleWithReasons( message: ParsedMessage, emailAccount: EmailAccountWithAI, client: EmailProvider, + modelType: ModelType, ): Promise<{ rule?: RuleWithActionsAndCategories; matchReasons?: MatchReason[]; @@ -248,6 +253,7 @@ async function findMatchingRuleWithReasons( email: getEmailForLLM(message), rules: potentialMatches, emailAccount, + modelType, }); return result; diff --git a/apps/web/utils/ai/choose-rule/run-rules.ts b/apps/web/utils/ai/choose-rule/run-rules.ts index a2ac71e38f..6c765ea202 100644 --- a/apps/web/utils/ai/choose-rule/run-rules.ts +++ b/apps/web/utils/ai/choose-rule/run-rules.ts @@ -22,6 +22,7 @@ import { } from "@/utils/scheduled-actions/scheduler"; import groupBy from "lodash/groupBy"; import type { EmailProvider } from "@/utils/email/provider"; +import type { ModelType } from "@/utils/llms/model"; const logger = createScopedLogger("ai-run-rules"); @@ -39,18 +40,21 @@ export async function runRules({ rules, emailAccount, isTest, + modelType, }: { client: EmailProvider; message: ParsedMessage; rules: RuleWithActionsAndCategories[]; emailAccount: EmailAccountWithAI; isTest: boolean; + modelType: ModelType; }): Promise { const result = await findMatchingRule({ rules, message, emailAccount, client, + modelType, }); analyzeSenderPatternIfAiMatch({ @@ -71,6 +75,7 @@ export async function runRules({ result.reason, result.matchReasons, isTest, + modelType, ); } else { await saveSkippedExecutedRule({ @@ -91,6 +96,7 @@ async function executeMatchedRule( reason: string | undefined, matchReasons: MatchReason[] | undefined, isTest: boolean, + modelType: ModelType, ) { // get action items with args const actionItems = await getActionItemsWithAiArgs({ @@ -98,6 +104,7 @@ async function executeMatchedRule( emailAccount, selectedRule: rule, client, + modelType, }); if (!isTest) { diff --git a/apps/web/utils/ai/reply/generate-nudge.ts b/apps/web/utils/ai/reply/generate-nudge.ts index e6f7e5a67e..e9bf751ff9 100644 --- a/apps/web/utils/ai/reply/generate-nudge.ts +++ b/apps/web/utils/ai/reply/generate-nudge.ts @@ -44,6 +44,7 @@ IMPORTANT: The person you're writing an email for is: ${messages.at(-1)?.from}.` system, prompt, userEmail: emailAccount.email, + modelType: "chat", usageLabel: "Reply", }); diff --git a/apps/web/utils/cold-email/is-cold-email.ts b/apps/web/utils/cold-email/is-cold-email.ts index 407f9ead7b..b75eab47fa 100644 --- a/apps/web/utils/cold-email/is-cold-email.ts +++ b/apps/web/utils/cold-email/is-cold-email.ts @@ -13,6 +13,7 @@ import { stringifyEmail } from "@/utils/stringify-email"; import { createScopedLogger } from "@/utils/logger"; import type { EmailForLLM } from "@/utils/types"; import type { EmailProvider } from "@/utils/email/provider"; +import type { ModelType } from "@/utils/llms/model"; const logger = createScopedLogger("ai-cold-email"); @@ -22,10 +23,12 @@ export async function isColdEmail({ email, emailAccount, provider, + modelType, }: { email: EmailForLLM & { threadId?: string }; emailAccount: Pick & EmailAccountWithAI; provider: EmailProvider; + modelType?: ModelType; }): Promise<{ isColdEmail: boolean; reason: ColdEmailBlockerReason; @@ -68,7 +71,7 @@ export async function isColdEmail({ } // otherwise run through ai to see if it's a cold email - const res = await aiIsColdEmail(email, emailAccount); + const res = await aiIsColdEmail(email, emailAccount, modelType); logger.info("AI is cold email?", { ...loggerOptions, @@ -87,10 +90,12 @@ export async function isColdEmailWithProvider({ email, emailAccount, provider, + modelType, }: { email: EmailForLLM & { threadId?: string }; emailAccount: Pick & EmailAccountWithAI; provider: EmailProvider; + modelType: ModelType; }): Promise<{ isColdEmail: boolean; reason: ColdEmailBlockerReason; @@ -133,7 +138,7 @@ export async function isColdEmailWithProvider({ } // otherwise run through ai to see if it's a cold email - const res = await aiIsColdEmail(email, emailAccount); + const res = await aiIsColdEmail(email, emailAccount, modelType); logger.info("AI is cold email?", { ...loggerOptions, @@ -170,6 +175,7 @@ async function isKnownColdEmailSender({ async function aiIsColdEmail( email: EmailForLLM, emailAccount: Pick & EmailAccountWithAI, + modelType?: ModelType, ) { const system = `You are an assistant that decides if an email is a cold email or not. @@ -208,6 +214,7 @@ ${stringifyEmail(email, 500)} }), userEmail: emailAccount.email, usageLabel: "Cold email check", + modelType, }); logger.trace("AI is cold email response", { response: response.object }); @@ -220,6 +227,7 @@ export async function runColdEmailBlockerWithProvider(options: { provider: EmailProvider; emailAccount: Pick & EmailAccountWithAI; + modelType: ModelType; }): Promise<{ isColdEmail: boolean; reason: ColdEmailBlockerReason; @@ -230,6 +238,7 @@ export async function runColdEmailBlockerWithProvider(options: { email: options.email, emailAccount: options.emailAccount, provider: options.provider, + modelType: options.modelType, }); if (!response.isColdEmail) return { ...response, coldEmailId: null }; diff --git a/version.txt b/version.txt index 852700e118..76b426fd03 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v2.1.0 \ No newline at end of file +v2.1.1 \ No newline at end of file