server auth stuff, prooompt engineering

This commit is contained in:
2026-03-31 15:29:47 +02:00
parent c5b3ee3875
commit ead9548744
6 changed files with 29 additions and 28 deletions

View File

@@ -8,7 +8,7 @@ export default function AssistantModalPage() {
const { data: session, error, isLoading } = trpc.chat.getSession.useQuery(); const { data: session, error, isLoading } = trpc.chat.getSession.useQuery();
return ( return (
<> <>
&& <ChatModal sessionId={session?.id} /> <ChatModal sessionId={session?.id} />
{error && <div>{error.message}</div>} {error && <div>{error.message}</div>}
{isLoading && <Skeleton />} {isLoading && <Skeleton />}
</> </>

View File

@@ -0,0 +1,8 @@
export default function currentTime() {
let now = Date.now();
console.log(now);
return {
success: true,
time: now
}
}

View File

@@ -1,5 +1,5 @@
'use server' 'use server'
import { clerkClient } from '@clerk/nextjs/server' import { clerkClient, auth } from '@clerk/nextjs/server'
import { google } from 'googleapis' import { google } from 'googleapis'
import { env } from '~/env' import { env } from '~/env'
@@ -10,7 +10,6 @@ export async function scheduleMeeting({
durationMinutes, durationMinutes,
attendeeEmail, attendeeEmail,
attendeeName, attendeeName,
userId,
}: { }: {
title: string title: string
description: string description: string
@@ -18,11 +17,11 @@ export async function scheduleMeeting({
durationMinutes: number durationMinutes: number
attendeeEmail?: string attendeeEmail?: string
attendeeName?: string attendeeName?: string
userId: string
}) { }) {
try { try {
const clerk = await clerkClient() const clerk = await clerkClient()
const userAuth = await auth()
const user = await clerk.users.getUser(userAuth.userId?userAuth.userId:"")
// Get admin's Google OAuth token to create the event on Gregor's calendar // Get admin's Google OAuth token to create the event on Gregor's calendar
const adminTokenResponse = await clerk.users.getUserOauthAccessToken( const adminTokenResponse = await clerk.users.getUserOauthAccessToken(
env.ADMIN_USER_CLERK_ID, env.ADMIN_USER_CLERK_ID,
@@ -37,16 +36,7 @@ export async function scheduleMeeting({
// Try to resolve visitor's Google email for the invite // Try to resolve visitor's Google email for the invite
let visitorEmail: string | undefined = attendeeEmail let visitorEmail: string | undefined = attendeeEmail
if (!visitorEmail) { if (!visitorEmail) {
try { visitorEmail = user?.emailAddresses.at(0)?.emailAddress ?? undefined
const visitorTokenResponse = await clerk.users.getUserOauthAccessToken(userId, 'oauth_google')
if (visitorTokenResponse.data[0]) {
const user = await clerk.users.getUser(userId)
const googleAccount = user.externalAccounts.find((a) => a.provider === 'google')
visitorEmail = googleAccount?.emailAddress ?? undefined
}
} catch {
// Visitor not signed in with Google — no invite
}
} }
const oAuth2Client = new google.auth.OAuth2() const oAuth2Client = new google.auth.OAuth2()
@@ -71,6 +61,7 @@ export async function scheduleMeeting({
end: { dateTime: endTime.toISOString(), timeZone: 'UTC' }, end: { dateTime: endTime.toISOString(), timeZone: 'UTC' },
attendees, attendees,
}, },
sendNotifications: true
}) })
return { return {

View File

@@ -8,6 +8,7 @@ import { db } from '~/server/db'
import { chatSession, chatMessage } from '~/server/dbschema/schema' import { chatSession, chatMessage } from '~/server/dbschema/schema'
import { servTrpc } from '~/app/_trpc/ServerClient' import { servTrpc } from '~/app/_trpc/ServerClient'
import { scheduleMeeting } from '~/app/actions/scheduleMeeting' import { scheduleMeeting } from '~/app/actions/scheduleMeeting'
import currentTime from '~/app/actions/currentTime';
const openai = createOpenAI({ apiKey: env.OPENAI_API_KEY }) const openai = createOpenAI({ apiKey: env.OPENAI_API_KEY })
@@ -45,7 +46,7 @@ export async function POST(req: Request) {
} }
const result = streamText({ const result = streamText({
model: openai('gpt-4o'), model: openai('gpt-5-mini'),
system: systemPrompt, system: systemPrompt,
messages: await convertToModelMessages(messages), messages: await convertToModelMessages(messages),
tools: { tools: {
@@ -64,22 +65,22 @@ export async function POST(req: Request) {
.int() .int()
.min(15) .min(15)
.max(120) .max(120)
.describe('Duration of the meeting in minutes'), .describe('Duration of the meeting in minutes, if none provided ask if 20 minutes is ok'),
attendeeEmail: z attendeeEmail: z
.string() .string()
.email() .email()
.optional() .optional()
.describe('Email of the visitor to invite (if provided)'), .describe('Optional Email of the visitor to invite (if provided)'),
attendeeName: z.string().optional().describe('Name of the visitor'), attendeeName: z.string().optional().describe('Name of the visitor'),
}), }),
execute: async (input) => scheduleMeeting({ ...input, userId }), execute: async (input) => scheduleMeeting({ ...input }),
}), }),
getCurrentUnixTime: tool({ getCurrentUnixTime: tool({
description: 'Get the current unix time to reference for meeting dates', description: 'Get the current unix time to reference for meeting dates',
inputSchema: z.object({ inputSchema: z.object({
none: z.string().optional().describe("no inputs are needed") none: z.string().optional().describe("no inputs are needed")
}), }),
execute: async () => { return {success: true, currentTime: Date.now()} } execute: async () => currentTime()
}) })
}, },
stopWhen: stepCountIs(5), stopWhen: stepCountIs(5),

View File

@@ -59,7 +59,7 @@ export default function ChatInterface({ sessionId }: ChatInterfaceProps) {
}) })
const handleSend = () => { const handleSend = () => {
const text = input.trim() const text = input.trim()
if (!text || status != 'ready') return if (!text || status != 'ready' || sessionId == undefined) return
setInput('') setInput('')
sendMessage({ text }) sendMessage({ text })
addMessage({ addMessage({
@@ -140,7 +140,7 @@ export default function ChatInterface({ sessionId }: ChatInterfaceProps) {
<div className='flex flex-col gap-2'> <div className='flex flex-col gap-2'>
<Button <Button
onClick={handleSend} onClick={handleSend}
disabled={status != "ready" || !input.trim()} disabled={status != "ready" || !input.trim() || sessionId == undefined}
> >
Send Send
</Button> </Button>

View File

@@ -1,4 +1,3 @@
import { auth } from '@clerk/nextjs/server'
import { publicProcedure, router } from "../trpc"; import { publicProcedure, router } from "../trpc";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import { db } from '~/server/db' import { db } from '~/server/db'
@@ -7,27 +6,29 @@ chatSession, systemSettings } from "../dbschema/schema";
import { isAdmin } from '~/app/actions'; import { isAdmin } from '~/app/actions';
import { z } from 'zod'; import { z } from 'zod';
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import { clerkClient, auth } from '@clerk/nextjs/server'
export const chatRouter = router({ export const chatRouter = router({
getSession: publicProcedure.query(async () => { getSession: publicProcedure.query(async () => {
const clerk = await clerkClient()
const { userId } = await auth(); const { userId } = await auth();
if (userId == null) { const user = await clerk.users.getUser(userId?userId:"")
if (user == undefined) {
throw new TRPCError({ message: "chat is only available to signed in users", code: 'UNAUTHORIZED' }); throw new TRPCError({ message: "chat is only available to signed in users", code: 'UNAUTHORIZED' });
} }
let session = await db.query.chatSession.findFirst({ let session = await db.query.chatSession.findFirst({
where(fields, operators) { where(fields, operators) {
return operators.eq(fields.userId, userId) return operators.eq(fields.userId, user.id)
}, },
}) })
if (session !== undefined) { if (session !== undefined) {
return session; return session;
} }
let newSession = await db.insert(chatSession).values({ userId: userId }).returning().execute().then((r) => r.at(0)); let newSession = await db.insert(chatSession).values({ userId: user.id}).returning().execute().then((r) => r.at(0)); if (newSession == undefined) {
if (newSession == undefined) {
throw new TRPCError({ message: "failed to create session", code: "INTERNAL_SERVER_ERROR" }); throw new TRPCError({ message: "failed to create session", code: "INTERNAL_SERVER_ERROR" });
} }
session = await db.query.chatSession.findFirst({ session = await db.query.chatSession.findFirst({
where(fields, operators) { where(fields, operators) {
return operators.eq(fields.userId, userId) return operators.eq(fields.userId, user.id)
}, },
}) })
if (session == undefined) { if (session == undefined) {