Skip to content

Commit

Permalink
Search for documents
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusab committed Sep 4, 2024
1 parent 2ec8420 commit b2d89ad
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 78 deletions.
6 changes: 5 additions & 1 deletion apps/dashboard/src/actions/ai/chat/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type { AIState, Chat, ClientMessage, UIState } from "../types";
import { getBurnRateTool } from "./tools/burn-rate";
import { getForecastTool } from "./tools/forecast";
import { getDocumentsTool } from "./tools/get-documents";
import { getInvoicesTool } from "./tools/get-invoces";
import { getTransactionsTool } from "./tools/get-transactions";
import { getProfitTool } from "./tools/profit";
import { createReport } from "./tools/report";
Expand Down Expand Up @@ -102,9 +103,11 @@ export async function submitUserMessage(
If the user just wants the runway, call \`getRunway\` function.
If the user just wants the profit, call \`getProfit\` function.
If the user just wants to find transactions, call \`getTransactions\` function.
If the user just wants to find documents, invoices or receipts, call \`getDocuments\` function.
If the user just wants to find invoices or receipts, call \`getInvoices\` function.
If the user just wants to find documents, call \`getDocuments\` function.
Always try to call the functions with default values, otherwise ask the user to respond with parameters. Just show one example if you can't call the function.
Current date is: ${new Date().toISOString()}
`,
messages: [
Expand Down Expand Up @@ -178,6 +181,7 @@ export async function submitUserMessage(
dateTo: defaultValues.to,
}),
getTransactions: getTransactionsTool({ aiState }),
getInvoices: getInvoicesTool({ aiState, teamId }),
getDocuments: getDocumentsTool({ aiState, teamId }),
createReport: createReport({
aiState,
Expand Down
23 changes: 14 additions & 9 deletions apps/dashboard/src/actions/ai/chat/tools/get-documents.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { MutableAIState } from "@/actions/ai/types";
import { getInboxSearchQuery } from "@midday/supabase/queries";
import { getVaultQuery } from "@midday/supabase/queries";
import { createClient } from "@midday/supabase/server";
import { nanoid } from "nanoid";
import { z } from "zod";
Expand All @@ -12,23 +12,28 @@ type Args = {

export function getDocumentsTool({ aiState, teamId }: Args) {
return {
description: "Find reciept or invoice",
description: "Find documents",
parameters: z.object({
name: z.string().describe("The name of the invoice or reciept"),
name: z.string().describe("The name of the document"),
}),
generate: async (args) => {
const { name, amount } = args;
const { name } = args;
const supabase = createClient();

const searchQuery = name || amount;

const data = await getInboxSearchQuery(supabase, {
const { data } = await getVaultQuery(supabase, {
teamId,
q: searchQuery,
searchQuery: name,
});

const formattedData = data?.map((item) => ({
...item,
content_type: item?.metadata?.mimetype,
display_name: item?.name,
file_path: item?.path_tokens,
}));

const props = {
data,
data: formattedData,
};

const toolCallId = nanoid();
Expand Down
70 changes: 70 additions & 0 deletions apps/dashboard/src/actions/ai/chat/tools/get-invoces.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { MutableAIState } from "@/actions/ai/types";
import { getInboxSearchQuery } from "@midday/supabase/queries";
import { createClient } from "@midday/supabase/server";
import { nanoid } from "nanoid";
import { z } from "zod";
import { DocumentsUI } from "./ui/documents-ui";

type Args = {
aiState: MutableAIState;
teamId: string;
};

export function getInvoicesTool({ aiState, teamId }: Args) {
return {
description: "Find reciept or invoice",
parameters: z.object({
name: z.string().describe("The name of the invoice or reciept"),
}),
generate: async (args) => {
const { name, amount } = args;
const supabase = createClient();

const searchQuery = name || amount;

const data = await getInboxSearchQuery(supabase, {
teamId,
q: searchQuery,
});

const props = {
data,
};

const toolCallId = nanoid();

aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages,
{
id: nanoid(),
role: "assistant",
content: [
{
type: "tool-call",
toolName: "getInvoices",
toolCallId,
args,
},
],
},
{
id: nanoid(),
role: "tool",
content: [
{
type: "tool-result",
toolName: "getInvoices",
toolCallId,
result: props,
},
],
},
],
});

return <DocumentsUI {...props} />;
},
};
}
1 change: 1 addition & 0 deletions apps/dashboard/src/components/chat/examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export const chatExamples = [
"Create a report",
"Forecast profit",
"Forecast revenue",
"Find a document",
];
134 changes: 67 additions & 67 deletions apps/dashboard/src/components/file-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,71 +122,7 @@ export function FilePreview({

return (
<Dialog>
<div className={cn(className, "relative h-full")}>
<AnimatePresence>
{!preview && isLoaded && (
<div className="absolute bottom-4 left-2 flex space-x-2 z-10">
{!disableFullscreen && (
<motion.div
initial={{ y: 50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -50, opacity: 0 }}
>
<DialogTrigger asChild>
<Button
variant="secondary"
className="w-[32px] h-[32px] bg-white/80 hover:bg-white dark:bg-black/80 dark:hover:bg-black border"
size="icon"
>
<Icons.OpenInFull />
</Button>
</DialogTrigger>
</motion.div>
)}

{downloadUrl && (
<motion.div
initial={{ y: 50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -50, opacity: 0 }}
transition={{ delay: 0.04 }}
>
<a href={downloadUrl} download>
<Button
variant="secondary"
className="w-[32px] h-[32px] bg-white/80 hover:bg-white dark:bg-black/80 dark:hover:bg-black border"
size="icon"
>
<Icons.FileDownload />
</Button>
</a>
</motion.div>
)}
</div>
)}

{disableFullscreen && download && downloadUrl && (
<div className="absolute bottom-4 left-2 z-10">
<motion.div
initial={{ y: 50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -50, opacity: 0 }}
transition={{ delay: 0.04 }}
>
<a href={downloadUrl} download>
<Button
variant="secondary"
className="w-[32px] h-[32px] bg-white/80 hover:bg-white dark:bg-black/80 dark:hover:bg-black border"
size="icon"
>
<Icons.FileDownload />
</Button>
</a>
</motion.div>
</div>
)}
</AnimatePresence>

<div className={cn(className, "relative h-full overflow-hidden")}>
<div
className={cn(
"w-full h-full flex items-center justify-center pointer-events-none",
Expand All @@ -208,10 +144,74 @@ export function FilePreview({
<div
className={cn(
"w-full h-full items-center flex justify-center bg-[#F2F1EF] dark:bg-secondary",
!isLoaded && "opacity-0",
error && "opacity-1 bg-transparent",
!isLoaded && "hidden",
error && "visible bg-transparent",
)}
>
<AnimatePresence>
{!preview && isLoaded && (
<div className="absolute bottom-4 left-2 flex space-x-2 z-10">
{!disableFullscreen && (
<motion.div
initial={{ y: 50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -50, opacity: 0 }}
>
<DialogTrigger asChild>
<Button
variant="secondary"
className="w-[32px] h-[32px] bg-white/80 hover:bg-white dark:bg-black/80 dark:hover:bg-black border"
size="icon"
>
<Icons.OpenInFull />
</Button>
</DialogTrigger>
</motion.div>
)}

{downloadUrl && (
<motion.div
initial={{ y: 50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -50, opacity: 0 }}
transition={{ delay: 0.04 }}
>
<a href={downloadUrl} download>
<Button
variant="secondary"
className="w-[32px] h-[32px] bg-white/80 hover:bg-white dark:bg-black/80 dark:hover:bg-black border"
size="icon"
>
<Icons.FileDownload />
</Button>
</a>
</motion.div>
)}
</div>
)}

{disableFullscreen && download && downloadUrl && (
<div className="absolute bottom-4 left-2 z-10">
<motion.div
initial={{ y: 50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -50, opacity: 0 }}
transition={{ delay: 0.04 }}
>
<a href={downloadUrl} download>
<Button
variant="secondary"
className="w-[32px] h-[32px] bg-white/80 hover:bg-white dark:bg-black/80 dark:hover:bg-black border"
size="icon"
>
<Icons.FileDownload />
</Button>
</a>
</motion.div>
</div>
)}
</AnimatePresence>

{error ? (
<Icons.Image size={16} />
) : (
Expand Down
5 changes: 4 additions & 1 deletion packages/supabase/src/queries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,10 @@ export async function getVaultQuery(supabase: Client, params: GetVaultParams) {
const { start, end, owners, tags } = filter || {};

const isSearch =
Object.values(filter).some((value) => value !== null) ||
(filter !== undefined &&
Object.values(filter).some(
(value) => value !== undefined && value !== null,
)) ||
Boolean(searchQuery);

const query = supabase
Expand Down

0 comments on commit b2d89ad

Please sign in to comment.