Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
2828a94
WIP: migrate to ai sdk v5. openrouter holding it up
elie222 Jul 6, 2025
3424d2f
comment
elie222 Jul 6, 2025
64050a7
Merge branch 'main' into chore/ai-sdk-v5
elie222 Jul 14, 2025
294285e
upgrade openrouter to work with ai sdk v5
elie222 Jul 14, 2025
6597855
update packages
elie222 Jul 14, 2025
9e47468
Fix up AI sdk onFinish and types
elie222 Jul 14, 2025
9713d87
try catch error
elie222 Jul 14, 2025
cefbbc8
clean up
elie222 Jul 14, 2025
47746ef
setInput type fix
elie222 Jul 14, 2025
7976f8b
fix ts
elie222 Jul 14, 2025
0cb6d90
fix ts
elie222 Jul 14, 2025
7f7682d
Merge branch 'chore/ai-sdk-v5' into feat/ai-sdk-v5
elie222 Jul 24, 2025
31e1efd
remove log
elie222 Jul 24, 2025
06f75d1
type
elie222 Jul 24, 2025
55a49fd
biome version
elie222 Jul 24, 2025
c6761a8
Merge branch 'staging' into feat/ai-sdk-v5
elie222 Jul 25, 2025
d10d434
Use faster model when interacting from within app
elie222 Aug 3, 2025
def4947
fix warning
elie222 Aug 3, 2025
bdf427b
fix failing tests
elie222 Aug 3, 2025
1407721
Merge branch 'main' into feat/ai-sdk-v5
elie222 Aug 3, 2025
c66c0d5
biome fixes
elie222 Aug 3, 2025
e746476
update to latest package versions
elie222 Aug 3, 2025
89123f4
update package
elie222 Aug 3, 2025
1bf7e4d
fix tool call cards not showing
elie222 Aug 3, 2025
2abbacd
better ui and logs
elie222 Aug 3, 2025
1045a91
fix null upstash error
elie222 Aug 3, 2025
bf4128f
fix create chat messages
elie222 Aug 3, 2025
90a5d6e
get initial messages to show
elie222 Aug 3, 2025
31046a2
create chat provider for entire app
elie222 Aug 4, 2025
f400292
chat id
elie222 Aug 4, 2025
d9dfcfb
biome fix
elie222 Aug 4, 2025
a2f6a41
fix label
elie222 Aug 4, 2025
f39f4d0
spacing
elie222 Aug 4, 2025
11a264d
clean up set chat message
elie222 Aug 4, 2025
0b4f026
move chat to right sidebar
elie222 Aug 4, 2025
bd633df
fix up sidebars
elie222 Aug 5, 2025
36d7dbd
styling
elie222 Aug 5, 2025
4bb455d
remove old code
elie222 Aug 5, 2025
414766b
fix build
elie222 Aug 5, 2025
c05ad9c
cookie fix
elie222 Aug 5, 2025
8f53e9a
code cleanup
elie222 Aug 5, 2025
d759683
fix build
elie222 Aug 5, 2025
5e4d583
delete file. fix logo
elie222 Aug 5, 2025
e77ce21
fix sidebar
elie222 Aug 5, 2025
4c16439
fix build
elie222 Aug 5, 2025
f3b39b4
build fixes
elie222 Aug 5, 2025
fff696d
remove warnings
elie222 Aug 5, 2025
ad4391f
fix sidebar tooltip when expanded
elie222 Aug 6, 2025
e362bf1
clean up types and object generation
elie222 Aug 6, 2025
6a0e1b9
fix types
elie222 Aug 6, 2025
b8dd4a6
fix ai calls and types
elie222 Aug 6, 2025
a61d5f8
add back schema name/desc
elie222 Aug 6, 2025
994735d
remove ollama test
elie222 Aug 6, 2025
57bf63f
v2.1.0
elie222 Aug 6, 2025
07434e2
Merge pull request #592 from elie222/feat/ai-sdk-v5
elie222 Aug 6, 2025
c9fd1b9
Merge branch 'main' into cursor/switch-to-chat-model-for-user-actions…
elie222 Aug 6, 2025
a8c9ce2
fix bug
elie222 Aug 6, 2025
acf0a02
v2.1.1
elie222 Aug 6, 2025
4d80009
Merge pull request #631 from elie222/cursor/switch-to-chat-model-for-…
elie222 Aug 6, 2025
38e5a97
fix up bad outputs
elie222 Aug 6, 2025
70a8c0a
fix up generation
elie222 Aug 6, 2025
a5c8d0c
fix prompt to rules
elie222 Aug 6, 2025
f265a10
fixes
elie222 Aug 6, 2025
57b23b1
dont use sonnet with openrouter
elie222 Aug 6, 2025
267a0a2
Merge branch 'main' into cursor/switch-to-chat-model-for-user-actions…
elie222 Aug 6, 2025
244ed7a
update prompts
elie222 Aug 6, 2025
2606f9c
fix tool call
elie222 Aug 6, 2025
8ef8934
fix ai call
elie222 Aug 6, 2025
c675337
fix build
elie222 Aug 6, 2025
bf75e32
Merge pull request #656 from elie222/cursor/switch-to-chat-model-for-…
elie222 Aug 6, 2025
ac009a8
fix build
elie222 Aug 6, 2025
facb791
fix build
elie222 Aug 6, 2025
6052565
fix build
elie222 Aug 6, 2025
0f1f7fd
ai generate object fixes
elie222 Aug 6, 2025
5f54c27
fix
elie222 Aug 7, 2025
66e58dc
Merge pull request #657 from elie222/cursor/switch-to-chat-model-for-…
elie222 Aug 7, 2025
f5a667e
revert to tool call
elie222 Aug 7, 2025
31069b4
add dev logging
elie222 Aug 7, 2025
77e0be7
refactor
elie222 Aug 7, 2025
154201f
generate object
elie222 Aug 7, 2025
efa9ea4
code cleanup
elie222 Aug 7, 2025
2813da6
fix snippets call
elie222 Aug 7, 2025
2972b2a
fix getmodel
elie222 Aug 7, 2025
256bd61
adjust error handling
elie222 Aug 7, 2025
b4bb746
fix up ai calls
elie222 Aug 7, 2025
16c34e8
use new llm functions
elie222 Aug 7, 2025
7754cb6
comment out backup model
elie222 Aug 7, 2025
46d76c6
backup model
elie222 Aug 7, 2025
dfdca1e
fix tests
elie222 Aug 7, 2025
fcce987
v2.2.0
elie222 Aug 7, 2025
1b27e7e
adjust logs
elie222 Aug 7, 2025
87779fc
formatting
elie222 Aug 7, 2025
f99b072
adjust prompt
elie222 Aug 7, 2025
0816a83
Merge pull request #660 from elie222/feat/ai-fixes
elie222 Aug 7, 2025
52657f0
fix save rules
elie222 Aug 7, 2025
c3a8dd8
fix ai diff and upgrade openrouter
elie222 Aug 7, 2025
4194483
Merge pull request #661 from elie222/feat/fix-ai-diff
elie222 Aug 7, 2025
4bd0f26
v2.2.2
elie222 Aug 7, 2025
4bc0674
Merge branch 'main' of github.com:elie222/inbox-zero into migrate-to-…
edulelis Aug 7, 2025
15d4dab
Cleanup unused props. Add linking on update
edulelis Aug 7, 2025
42f724b
PR feedback
edulelis Aug 7, 2025
a800c2c
PR Feedback
edulelis Aug 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 10 additions & 30 deletions .cursor/rules/llm.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -28,39 +28,20 @@ import { z } from "zod";
import { createScopedLogger } from "@/utils/logger";
import { chatCompletionObject } from "@/utils/llms";
import type { UserEmailWithAI } from "@/utils/llms/types";
// Import other necessary types and utilities

// 1. Create a scoped logger
const logger = createScopedLogger("feature-name");

// 2. Define output schema with zod
export const schema = z.object({
// Define expected response fields
field1: z.string(),
field2: z.number(),
nested: z.object({
subfield: z.string(),
}),
array_field: z.array(z.string()),
});

// 3. Create main function with typed options

export async function featureFunction(options: {
inputData: InputType;
user: UserEmailWithAI;
}) {
const { inputData, user } = options;

// 4. Add early validation/returns
if (!inputData || [other validation conditions]) {
logger.warn("Invalid input for feature function");
return null;
}

// 5. Define system prompt
const system = `[Detailed system prompt that defines the LLM's role and task]`;

// 6. Construct user prompt
const prompt = `[User prompt with context and specific instructions]

<data>
Expand All @@ -69,24 +50,23 @@ export async function featureFunction(options: {

${user.about ? `<user_info>${user.about}</user_info>` : ""}`;

// 7. Log inputs
logger.trace("Input", { system, prompt });

// 8. Call LLM with proper configuration
const result = await chatCompletionObject({
userAi: user,
system,
prompt,
schema,
schema: z.object({
field1: z.string(),
field2: z.number(),
nested: z.object({
subfield: z.string(),
}),
array_field: z.array(z.string()),
}),
userEmail: user.email,
usageLabel: "Feature Name",
});

// 9. Log outputs
logger.trace("Output", { result });

// 10. Return validated result
return result.object;
return result.object;
}
```

Expand Down
9 changes: 5 additions & 4 deletions apps/web/__tests__/ai-diff-rules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { describe, it, expect, vi } from "vitest";
import { aiDiffRules } from "@/utils/ai/rule/diff-rules";
import { getEmailAccount } from "@/__tests__/helpers";

// pnpm test-ai ai-diff-rules
// RUN_AI_TESTS=true pnpm test-ai ai-diff-rules

const isAiTest = process.env.RUN_AI_TESTS === "true";

Expand Down Expand Up @@ -33,13 +33,14 @@ describe.runIf(isAiTest)("aiDiffRules", () => {
});

expect(result).toEqual({
addedRules: ['Label all emails from support@company.com as "Support"'],
addedRules: ['* Label all emails from support@company.com as "Support"'],
editedRules: [
{
oldRule: `Archive all newsletters and label them "Newsletter"`,
newRule: `Archive all newsletters and label them "Newsletter Updates"`,
oldRule: `* Archive all newsletters and label them "Newsletter"`,
newRule: `* Archive all newsletters and label them "Newsletter Updates"`,
},
],
removedRules: [`* Label receipts as "Receipt"`],
});
}, 15_000);

Expand Down
20 changes: 20 additions & 0 deletions apps/web/app/(app)/[emailAccountId]/assistant/AIChatButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use client";

import { Button } from "@/components/ui/button";
import { useSidebar } from "@/components/ui/sidebar";
import { MessageCircleIcon } from "lucide-react";

export function AIChatButton() {
const { setOpen } = useSidebar();

return (
<Button
size="sm"
variant="primaryBlue"
onClick={() => setOpen(["chat-sidebar"])}
>
<MessageCircleIcon className="mr-2 size-4" />
AI Chat
</Button>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import type { ParsedMessage } from "@/utils/types";
import { ViewEmailButton } from "@/components/ViewEmailButton";
import { ExecutedRuleStatus } from "@prisma/client";
import { FixWithChat } from "@/app/(app)/[emailAccountId]/assistant/FixWithChat";
import type { SetInputFunction } from "@/components/assistant-chat/types";
import { useAssistantNavigation } from "@/hooks/useAssistantNavigation";
import { useAccount } from "@/providers/EmailAccountProvider";

Expand Down Expand Up @@ -74,7 +73,7 @@ export function RuleCell({
status: ExecutedRuleStatus;
reason?: string | null;
message: ParsedMessage;
setInput: SetInputFunction;
setInput: (input: string) => void;
}) {
const { createAssistantUrl } = useAssistantNavigation(emailAccountId);

Expand Down
48 changes: 23 additions & 25 deletions apps/web/app/(app)/[emailAccountId]/assistant/FixWithChat.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { useRouter } from "next/navigation";
import { useQueryState } from "nuqs";
import { MessageCircleIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import type { SetInputFunction } from "@/components/assistant-chat/types";
import type { ParsedMessage } from "@/utils/types";
import type { RunRulesResult } from "@/utils/ai/choose-rule/run-rules";
import { truncate } from "@/utils/string";
Expand All @@ -18,28 +15,30 @@ import { useRules } from "@/hooks/useRules";
import { useAccount } from "@/providers/EmailAccountProvider";
import { useModal } from "@/hooks/useModal";
import { NEW_RULE_ID } from "@/app/(app)/[emailAccountId]/assistant/consts";
import { useAssistantNavigation } from "@/hooks/useAssistantNavigation";
import { Label } from "@/components/Input";
import { ButtonList } from "@/components/ButtonList";
import type { RulesResponse } from "@/app/api/user/rules/route";
import { ProcessResultDisplay } from "@/app/(app)/[emailAccountId]/assistant/ProcessResultDisplay";
import { NONE_RULE_ID } from "@/app/(app)/[emailAccountId]/assistant/consts";
import { useSidebar } from "@/components/ui/sidebar";

export function FixWithChat({
setInput,
message,
result,
}: {
setInput: SetInputFunction;
setInput: (input: string) => void;
message: ParsedMessage;
result: RunRulesResult | null;
}) {
const { data, isLoading, error } = useRules();
const { emailAccountId } = useAccount();
const { isModalOpen, setIsModalOpen } = useModal();
const { createAssistantUrl } = useAssistantNavigation(emailAccountId);
const router = useRouter();
const [currentTab] = useQueryState("tab");
// const { createAssistantUrl } = useAssistantNavigation(emailAccountId);
// const router = useRouter();
// const [currentTab] = useQueryState("tab");

const { setOpen } = useSidebar();

return (
<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
Expand Down Expand Up @@ -82,23 +81,22 @@ export function FixWithChat({
});
}

if (setInput) {
// this is only set if we're in the correct context
setInput(input);
} else {
// redirect to the assistant page
const searchParams = new URLSearchParams();
searchParams.set("input", input);
if (currentTab) searchParams.set("tab", currentTab);

router.push(
createAssistantUrl({
input,
tab: currentTab || undefined,
path: `/assistant${searchParams.toString()}`,
}),
);
}
setInput(input);
setOpen(["chat-sidebar"]);
// } else {
// // redirect to the assistant page
// const searchParams = new URLSearchParams();
// searchParams.set("input", input);
// if (currentTab) searchParams.set("tab", currentTab);

// router.push(
// createAssistantUrl({
// input,
// tab: currentTab || undefined,
// path: `/assistant${searchParams.toString()}`,
// }),
// );
// }

setIsModalOpen(false);
}}
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/(app)/[emailAccountId]/assistant/History.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { TablePagination } from "@/components/TablePagination";
import { Badge } from "@/components/Badge";
import { RulesSelect } from "@/app/(app)/[emailAccountId]/assistant/RulesSelect";
import { useAccount } from "@/providers/EmailAccountProvider";
import { useChat } from "@/components/assistant-chat/ChatContext";
import { useChat } from "@/providers/ChatProvider";

export function History() {
const [page] = useQueryState("page", parseAsInteger.withDefault(1));
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/(app)/[emailAccountId]/assistant/Pending.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { Checkbox } from "@/components/Checkbox";
import { useToggleSelect } from "@/hooks/useToggleSelect";
import { RulesSelect } from "@/app/(app)/[emailAccountId]/assistant/RulesSelect";
import { useAccount } from "@/providers/EmailAccountProvider";
import { useChat } from "@/components/assistant-chat/ChatContext";
import { useChat } from "@/providers/ChatProvider";

export function Pending() {
const [page] = useQueryState("page", parseAsInteger.withDefault(1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ import { ProcessResultDisplay } from "@/app/(app)/[emailAccountId]/assistant/Pro
import { Tooltip } from "@/components/Tooltip";
import { useAccount } from "@/providers/EmailAccountProvider";
import { FixWithChat } from "@/app/(app)/[emailAccountId]/assistant/FixWithChat";
import { useChat } from "@/components/assistant-chat/ChatContext";
import type { SetInputFunction } from "@/components/assistant-chat/types";
import { useChat } from "@/providers/ChatProvider";

type Message = MessagesResponse["messages"][number];

Expand Down Expand Up @@ -318,7 +317,7 @@ function ProcessRulesRow({
onRun: (rerun?: boolean) => void;
testMode: boolean;
emailAccountId: string;
setInput: SetInputFunction;
setInput: (input: string) => void;
}) {
return (
<TableRow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ function RulesPromptForm({

if (result?.data?.rulesPrompt) {
editorRef.current?.appendText(
result?.data?.rulesPrompt,
`\n${result?.data?.rulesPrompt || ""}`,
);
} else {
toast.error("Error generating prompt");
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/(app)/[emailAccountId]/assistant/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default async function AssistantPage({
<PermissionsCheck />

<div className="flex h-[calc(100vh-theme(spacing.16))] flex-col">
<Chat emailAccountId={emailAccountId} />
<Chat />
</div>
</Suspense>
</EmailProvider>
Expand Down
12 changes: 4 additions & 8 deletions apps/web/app/(app)/[emailAccountId]/automation/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Suspense } from "react";
import { cookies } from "next/headers";
import Link from "next/link";
import { redirect } from "next/navigation";
import { MessageCircleIcon, SlidersIcon } from "lucide-react";
import { SlidersIcon } from "lucide-react";
import prisma from "@/utils/prisma";
import { History } from "@/app/(app)/[emailAccountId]/assistant/History";
import { Pending } from "@/app/(app)/[emailAccountId]/assistant/Pending";
Expand All @@ -20,6 +20,7 @@ import { SettingsTab } from "@/app/(app)/[emailAccountId]/assistant/settings/Set
import { PageHeading } from "@/components/Typography";
import { TabSelect } from "@/components/TabSelect";
import { RulesTab } from "@/app/(app)/[emailAccountId]/assistant/RulesTab";
import { AIChatButton } from "@/app/(app)/[emailAccountId]/assistant/AIChatButton";

export const maxDuration = 300; // Applies to the actions

Expand Down Expand Up @@ -85,7 +86,7 @@ export default async function AutomationPage({
<Suspense>
<PermissionsCheck />

<div className="mx-4">
<div className="mx-4 mt-2">
<div className="w-screen-xl mx-auto max-w-screen-xl">
<div className="w-full">
<PremiumAlertWithData className="mb-2" />
Expand Down Expand Up @@ -203,12 +204,7 @@ function ExtraActions({ emailAccountId }: { emailAccountId: string }) {
buttonProps={{ size: "sm", variant: "ghost" }}
/>

<Button size="sm" variant="primaryBlue" asChild>
<Link href={prefixPath(emailAccountId, "/assistant")}>
<MessageCircleIcon className="mr-2 size-4" />
AI Chat
</Link>
</Button>
<AIChatButton />
</div>
);
}
2 changes: 1 addition & 1 deletion apps/web/app/(app)/[emailAccountId]/simple/Summary.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useCompletion } from "ai/react";
import { useCompletion } from "@ai-sdk/react";
import { useEffect } from "react";
import { ButtonLoader } from "@/components/Loading";
import { ViewMoreButton } from "@/app/(app)/[emailAccountId]/simple/ViewMoreButton";
Expand Down
8 changes: 2 additions & 6 deletions apps/web/app/(app)/[emailAccountId]/usage/usage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,9 @@ import { usePremium } from "@/components/PremiumAlert";
import { LoadingContent } from "@/components/LoadingContent";
import { env } from "@/env";
import { isPremium } from "@/utils/premium";
import type { RedisUsage } from "@/utils/redis/usage";

export function Usage(props: {
usage?: {
openaiCalls: number;
openaiTokensUsed: number;
} | null;
}) {
export function Usage(props: { usage: RedisUsage | null }) {
const { premium, isLoading, error } = usePremium();

return (
Expand Down
4 changes: 2 additions & 2 deletions apps/web/app/(app)/accounts/AddAccount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function AddAccount() {
const handleConnectGoogle = async () => {
await signIn.social({
provider: "google",
callbackURL: "/welcome",
callbackURL: "/accounts",
scopes: [...GMAIL_SCOPES],
});
};
Expand All @@ -40,7 +40,7 @@ export function AddAccount() {
const handleConnectMicrosoft = async () => {
await signIn.social({
provider: "microsoft",
callbackURL: "/welcome",
callbackURL: "/accounts",
scopes: [...OUTLOOK_SCOPES],
});
};
Expand Down
9 changes: 4 additions & 5 deletions apps/web/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import "../../styles/globals.css";
import type React from "react";
import { cookies } from "next/headers";
import { Suspense } from "react";
import dynamic from "next/dynamic";
import { redirect } from "next/navigation";
import { SideNavWithTopNav } from "@/components/SideNavWithTopNav";

Expand Down Expand Up @@ -40,7 +39,7 @@ export default async function AppLayout({
if (!session?.user.email) redirect("/login");

const cookieStore = await cookies();
const isClosed = cookieStore.get("sidebar_state")?.value === "false";
const isClosed = cookieStore.get("left-sidebar:state")?.value === "false";

return (
<AppProviders>
Expand All @@ -59,12 +58,12 @@ export default async function AppLayout({
<Suspense>
<LastLogin email={session.user.email} />
</Suspense>
<Suspense>
{/* <Suspense>
<CrispWithNoSSR email={session.user.email} />
</Suspense>
</Suspense> */}
</ErrorBoundary>
</AppProviders>
);
}

const CrispWithNoSSR = dynamic(() => import("@/components/CrispChat"));
// const CrispWithNoSSR = dynamic(() => import("@/components/CrispChat"));
Loading
Loading