Skip to content

Commit d8dba4f

Browse files
authored
Merge pull request #65 from atomly/feature/journl-agent-cleanup
feat(journal-entries): cleaned up z-index & client-side tooling
2 parents 3defe7a + 3f38432 commit d8dba4f

File tree

16 files changed

+309
-122
lines changed

16 files changed

+309
-122
lines changed

apps/web/src/ai/tools/manipulate-editor.client.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
"use client";
22

33
import { getAIExtension } from "@blocknote/xl-ai";
4+
import { useDrawer } from "~/components/ui/drawer";
45
import { useJournlAgentAwareness } from "../agents/use-journl-agent-awareness";
56
import { createClientTool } from "../utils/create-client-tool";
67
import { zManipulateEditorInput } from "./manipulate-editor.schema";
78

89
export function useManipulateEditorTool() {
910
const { getEditors } = useJournlAgentAwareness();
11+
const { closeDrawer } = useDrawer();
1012
const tool = createClientTool({
1113
execute: async (toolCall, chat) => {
1214
try {
@@ -25,7 +27,7 @@ export function useManipulateEditorTool() {
2527

2628
const aiExtension = getAIExtension(editor);
2729

28-
const result = await aiExtension.callLLM({
30+
const response = await aiExtension.callLLM({
2931
onBlockUpdate: (block) => {
3032
const blockElement = document.querySelector(`[data-id="${block}"]`);
3133
if (blockElement && !isElementPartiallyInViewport(blockElement)) {
@@ -35,15 +37,20 @@ export function useManipulateEditorTool() {
3537
userPrompt: toolCall.input.userPrompt,
3638
});
3739

38-
const changes = await result?.llmResult.streamObjectResult?.object;
40+
const stream = response?.llmResult.streamObjectResult;
41+
const changes = await stream?.object;
3942

4043
void chat.addToolResult({
41-
output: `The following changes were made to the editor: ${JSON.stringify(
42-
changes,
43-
)}`,
44+
output: changes
45+
? `The following changes were made: ${JSON.stringify(changes)}`
46+
: "Something went wrong and no changes were made.",
4447
tool: toolCall.toolName,
4548
toolCallId: toolCall.toolCallId,
4649
});
50+
51+
if (changes) {
52+
closeDrawer();
53+
}
4754
} catch (error) {
4855
void chat.addToolResult({
4956
output: `Error when calling the tool: ${error}`,

apps/web/src/ai/tools/navigate-journal-entry.client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
"use client";
22

33
import { useRouter } from "next/navigation";
4+
import { useDrawer } from "~/components/ui/drawer";
45
import { createClientTool } from "../utils/create-client-tool";
56
import { zNavigateJournalEntryInput } from "./navigate-journal-entry.schema";
67

78
export function useNavigateJournalEntryTool() {
89
const router = useRouter();
10+
const { closeDrawer } = useDrawer();
911
const tool = createClientTool({
1012
execute: (toolCall, chat) => {
1113
const entry = `/journal/${toolCall.input.date}`;
@@ -15,6 +17,7 @@ export function useNavigateJournalEntryTool() {
1517
tool: toolCall.toolName,
1618
toolCallId: toolCall.toolCallId,
1719
});
20+
closeDrawer();
1821
},
1922
inputSchema: zNavigateJournalEntryInput,
2023
name: "navigateJournalEntry",

apps/web/src/ai/tools/navigate-page.client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
"use client";
22

33
import { useRouter } from "next/navigation";
4+
import { useDrawer } from "~/components/ui/drawer";
45
import { createClientTool } from "../utils/create-client-tool";
56
import { zNavigatePageInput } from "./navigate-page.schema";
67

78
export function useNavigatePageTool() {
89
const router = useRouter();
10+
const { closeDrawer } = useDrawer();
911
const tool = createClientTool({
1012
execute: (toolCall, chat) => {
1113
const page = `/pages/${toolCall.input.id}`;
@@ -15,6 +17,7 @@ export function useNavigatePageTool() {
1517
tool: toolCall.toolName,
1618
toolCallId: toolCall.toolCallId,
1719
});
20+
closeDrawer();
1821
},
1922
inputSchema: zNavigatePageInput,
2023
name: "navigatePage",

apps/web/src/app/(app)/@appSidebar/_components/app-sidebar-user.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export async function AppSidebarUser() {
4949
</SidebarMenuButton>
5050
</DropdownMenuTrigger>
5151
<AppSidebarUserMenu
52+
data-name="app-sidebar-user-menu"
5253
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
5354
align="end"
5455
sideOffset={4}

apps/web/src/app/(app)/@chatDrawer/default.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ export default function ChatDrawer() {
3737
</div>
3838
</div>
3939
</DrawerTrigger>
40-
<DrawerContent className="!h-full !max-h-[90dvh]">
40+
<DrawerContent className="!h-full !max-h-[90dvh] z-4500">
4141
<DrawerTitle className="hidden">Journl</DrawerTitle>
4242
<div className="!h-full relative">
43-
<DrawerDivider className="-translate-x-1/2 absolute top-0 left-1/2 z-50" />
43+
<DrawerDivider className="-translate-x-1/2 absolute top-0 left-1/2 z-4500" />
4444
<ThreadPrimitive.Root
4545
className="relative box-border flex h-full flex-col overflow-hidden bg-sidebar pt-6"
4646
style={{

apps/web/src/app/(app)/globals.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,30 @@
11
@import "../styles/blocknote.css";
2+
3+
/*
4+
Z-Index Hierarchy
5+
6+
The following is the z-index hierarchy for the app.
7+
The only component we need to edit in this file is the overlay.
8+
The rest of the components are set by the Tailwind CSS classes, or by our editor framework.
9+
10+
- Sidebar Drag Handles (`z-index`: 6000)
11+
- Auth Settings Modal (`z-index`: 5000)
12+
- Header Search Modal and Overlay (`z-index`: 5000)
13+
- Auth Settings Dropdown (`z-index`: 4500)
14+
- Drawer (with its overlay and drag handles)(`z-index`: 4500)
15+
- Editor Options/Drag Handle (`z-index`: 4000)
16+
- Editor Toolbar (`z-index`: 3000)
17+
- Editor AI Review (`z-index`: 3000)
18+
- Editor Menu (`z-index`: 2000)
19+
- Thread Composer Sources (`z-index`: 1600)
20+
- Sidebars (`z-index`: 1500)
21+
- Header (none)
22+
- Main Content (none)
23+
*/
24+
/* NOTE: This affects all dialog overlays */
25+
[data-slot="dialog-overlay"] {
26+
z-index: 5000;
27+
}
28+
[data-radix-popper-content-wrapper]:has([data-name="app-sidebar-user-menu"]) {
29+
z-index: 4500 !important;
30+
}

apps/web/src/app/(app)/layout.tsx

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { ThemeProvider } from "next-themes";
21
import { withAuth } from "~/auth/guards";
32
import { SidebarInset, SidebarProvider } from "~/components/ui/sidebar";
43
import { Toaster } from "~/components/ui/toast";
5-
import { TRPCReactProvider } from "~/trpc/react";
64
import ChatSidebarTrigger from "./@chatSidebar/_components/chat-sidebar-trigger";
75
import "./globals.css";
86
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
@@ -25,33 +23,24 @@ function AppLayout({
2523
header,
2624
}: AppLayoutProps) {
2725
return (
28-
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
29-
<AppProviders>
30-
<TRPCReactProvider>
31-
<SidebarProvider className="flex min-h-screen-safe flex-col">
32-
<div className="flex flex-1">
33-
<SidebarProvider defaultOpen={false}>
34-
{appSidebar}
35-
<SidebarInset className="flex max-h-svh flex-col">
36-
{header}
37-
<div className="min-w-54 flex-1 overflow-auto">
38-
{children}
39-
</div>
40-
<div className="mt-auto">{chatDrawer}</div>
41-
</SidebarInset>
42-
</SidebarProvider>
43-
{chatSidebar}
44-
<ChatSidebarTrigger />
45-
</div>
26+
<AppProviders>
27+
<SidebarProvider className="flex min-h-screen-safe flex-col">
28+
<div className="flex flex-1">
29+
<SidebarProvider defaultOpen={false}>
30+
{appSidebar}
31+
<SidebarInset className="flex max-h-svh flex-col">
32+
{header}
33+
<div className="min-w-54 flex-1 overflow-auto">{children}</div>
34+
<div className="mt-auto">{chatDrawer}</div>
35+
</SidebarInset>
4636
</SidebarProvider>
47-
<Toaster />
48-
<ReactQueryDevtools
49-
buttonPosition="bottom-left"
50-
initialIsOpen={false}
51-
/>
52-
</TRPCReactProvider>
53-
</AppProviders>
54-
</ThemeProvider>
37+
{chatSidebar}
38+
<ChatSidebarTrigger />
39+
</div>
40+
</SidebarProvider>
41+
<Toaster />
42+
<ReactQueryDevtools buttonPosition="bottom-left" initialIsOpen={false} />
43+
</AppProviders>
5544
);
5645
}
5746

apps/web/src/app/@authModal/(.)auth/[pathname]/_components/auth-view.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ export async function AuthView({ pathname }: { pathname: string }) {
2121
return (
2222
<AuthCard
2323
pathname={pathname}
24-
// className="flex w-full max-w-sm flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm"
25-
2624
className="z-10 w-full flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm"
2725
classNames={{
2826
base: "bg-transparent border-none",
@@ -38,6 +36,7 @@ export async function AuthView({ pathname }: { pathname: string }) {
3836
trigger: "md:hidden",
3937
},
4038
sidebar: {
39+
base: "gap-y-2",
4140
button: "cursor-pointer text-primary",
4241
buttonActive:
4342
"cursor-pointer border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,31 @@
11
"use client";
22

33
import { MantineProvider } from "@mantine/core";
4+
import { ThemeProvider } from "next-themes";
45
import { JournlAgentAwarenessProvider } from "~/ai/agents/use-journl-agent-awareness";
56
import { ThreadRuntime } from "~/components/assistant-ui/thread-runtime";
7+
import { DrawerProvider } from "~/components/ui/drawer";
8+
import { TRPCReactProvider } from "~/trpc/react";
69

710
export function AppProviders({ children }: { children: React.ReactNode }) {
811
return (
9-
<MantineProvider>
10-
<JournlAgentAwarenessProvider>
11-
<ThreadRuntime
12-
transport={{
13-
api: "/api/ai/journl-agent",
14-
}}
15-
messages={[]}
16-
>
17-
{children}
18-
</ThreadRuntime>
19-
</JournlAgentAwarenessProvider>
20-
</MantineProvider>
12+
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
13+
<MantineProvider>
14+
<JournlAgentAwarenessProvider>
15+
<TRPCReactProvider>
16+
<DrawerProvider>
17+
<ThreadRuntime
18+
transport={{
19+
api: "/api/ai/journl-agent",
20+
}}
21+
messages={[]}
22+
>
23+
{children}
24+
</ThreadRuntime>
25+
</DrawerProvider>
26+
</TRPCReactProvider>
27+
</JournlAgentAwarenessProvider>
28+
</MantineProvider>
29+
</ThemeProvider>
2130
);
2231
}

apps/web/src/app/api/ai/blocknote/[...all]/route.ts

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,41 @@
11
import type { NextRequest } from "next/server";
2+
import z from "zod";
23
import { handler as corsHandler } from "~/app/api/_cors/cors";
34
import { getSession } from "~/auth/server";
45
import { env } from "~/env";
6+
import { api } from "~/trpc/server";
57

6-
// import { api } from "~/trpc/server";
8+
const OPENAI_MESSAGE_PREFIX = "data: ";
9+
const OPENAI_MESSAGE_DONE_TEXT = "[DONE]";
10+
const OPENAI_MODEL_PROVIDER = "openai";
711

8-
const OPENAI_API_URL = "https://api.openai.com/v1/";
12+
const zChatCompletionMessage = z.object({
13+
choices: z.array(z.unknown()),
14+
created: z.number(),
15+
id: z.string(),
16+
model: z.string(),
17+
obfuscation: z.string(),
18+
object: z.literal("chat.completion.chunk"),
19+
service_tier: z.string(),
20+
system_fingerprint: z.string(),
21+
usage: z
22+
.object({
23+
completion_tokens: z.number(),
24+
completion_tokens_details: z.object({
25+
accepted_prediction_tokens: z.number(),
26+
audio_tokens: z.number(),
27+
reasoning_tokens: z.number(),
28+
rejected_prediction_tokens: z.number(),
29+
}),
30+
prompt_tokens: z.number(),
31+
prompt_tokens_details: z.object({
32+
audio_tokens: z.number(),
33+
cached_tokens: z.number(),
34+
}),
35+
total_tokens: z.number(),
36+
})
37+
.nullable(),
38+
});
939

1040
async function handler(req: NextRequest) {
1141
const session = await getSession();
@@ -22,7 +52,7 @@ async function handler(req: NextRequest) {
2252
return new Response("Not found", { status: 404 });
2353
}
2454

25-
const openAIResponse = await fetch(new URL(url, OPENAI_API_URL), {
55+
const openAIResponse = await fetch(new URL(url, env.OPENAI_API_URL), {
2656
body: JSON.stringify({
2757
...requestBody,
2858
stream: true,
@@ -40,22 +70,46 @@ async function handler(req: NextRequest) {
4070

4171
const transformStream = new TransformStream({
4272
async transform(chunk, controller) {
43-
const text = new TextDecoder().decode(chunk);
44-
45-
// TODO: Parse chunk and determine if it's a usage chunk.
46-
console.log("Intercepted chunk:", text);
47-
48-
// ! TODO: Track usage.
49-
// api.usage.trackModelUsage({
50-
// model: requestBody.model,
51-
// provider: requestBody.provider,
52-
// user_id: session.user.id,
53-
// quantity: requestBody.stream_options.include_usage ? 1 : 0,
54-
// unit: "output_tokens",
55-
// });
56-
57-
// TODO: Do not forward the usage chunk.
58-
controller.enqueue(chunk);
73+
try {
74+
const text = new TextDecoder().decode(chunk);
75+
76+
if (text === `${OPENAI_MESSAGE_PREFIX}${OPENAI_MESSAGE_DONE_TEXT}`) {
77+
return controller.enqueue(chunk);
78+
}
79+
80+
// Parsing the messages from the openAI response by removing the newline characters and the prefix.
81+
const data = text
82+
.replace(/[\r\n]+/g, "")
83+
.trim()
84+
.split(OPENAI_MESSAGE_PREFIX)
85+
.filter((t) => t !== "" && t !== OPENAI_MESSAGE_DONE_TEXT);
86+
87+
for (const json of data) {
88+
const message = zChatCompletionMessage.parse(JSON.parse(json));
89+
90+
if (!message.usage) continue;
91+
92+
await api.usage.trackModelUsage({
93+
metrics: [
94+
{
95+
quantity: message.usage.prompt_tokens,
96+
unit: "input_tokens",
97+
},
98+
{
99+
quantity: message.usage.completion_tokens,
100+
unit: "output_tokens",
101+
},
102+
],
103+
model_id: message.model,
104+
model_provider: OPENAI_MODEL_PROVIDER,
105+
user_id: session.user.id,
106+
});
107+
}
108+
109+
controller.enqueue(chunk);
110+
} catch (error) {
111+
console.error("Error tracking model usage", error);
112+
}
59113
},
60114
});
61115

0 commit comments

Comments
 (0)