Skip to content

Commit

Permalink
feat: add executeActionsOnFileBasedOnPrompt tool
Browse files Browse the repository at this point in the history
- Add new tool definition in chat/route.ts
- Create execute-actions-handler.tsx for content-based file actions
- Register handler in tool-invocation-handler.tsx
- Support tagging, folder organization, and renaming based on content

Co-Authored-By: ben <ben@prologe.io>
  • Loading branch information
devin-ai-integration[bot] and benjaminshafii committed Dec 24, 2024
1 parent ffeabfc commit c512809
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React, { useState } from "react";
import { TFile } from "obsidian";
import { ToolHandlerProps } from "./types";
import { usePlugin } from "../../organizer/provider";

export function ExecuteActionsHandler({ toolInvocation, handleAddResult, app }: ToolHandlerProps) {
const plugin = usePlugin();
const [isDone, setIsDone] = useState(false);
const [results, setResults] = useState<string[]>([]);
const [isProcessing, setIsProcessing] = useState(false);

const determineAction = (userPrompt: string): 'tags' | 'folders' | 'name' => {
const prompt = userPrompt.toLowerCase();
if (prompt.includes('tag') || prompt.includes('label')) {
return 'tags';
}
if (prompt.includes('folder') || prompt.includes('move') || prompt.includes('organize')) {
return 'folders';
}
return 'name'; // Default to rename if no specific action mentioned
};

const handleExecute = async () => {
try {
setIsProcessing(true);
const { filePaths, userPrompt } = toolInvocation.args;
const actionResults: string[] = [];
const action = determineAction(userPrompt);

for (const filePath of filePaths) {
try {
const file = plugin.app.vault.getAbstractFileByPath(filePath) as TFile;
if (!file) {
actionResults.push(`❌ File not found: ${filePath}`);
continue;
}

const content = await plugin.app.vault.read(file);

switch (action) {
case 'tags': {
const existingTags = plugin.app.metadataCache.getFileCache(file)?.tags?.map(t => t.tag) || [];
const suggestions = await plugin.recommendTags(content, filePath, existingTags);
if (suggestions.length > 0) {
const topTag = suggestions[0].tag;
await plugin.appendTag(file, topTag);
actionResults.push(`✅ Added tag ${topTag} to ${file.name}`);
} else {
actionResults.push(`ℹ️ No tag suggestions for ${file.name}`);
}
break;
}
case 'folders': {
const suggestions = await plugin.recommendFolders(content, file.name);
if (suggestions.length > 0) {
const topFolder = suggestions[0].folder;
await plugin.moveFile(file, topFolder);
actionResults.push(`✅ Moved ${file.name} to ${topFolder}`);
} else {
actionResults.push(`ℹ️ No folder suggestions for ${file.name}`);
}
break;
}
case 'name': {
const suggestions = await plugin.recommendName(content, file.name);
if (suggestions.length > 0) {
const newName = suggestions[0].title;
const newPath = `${file.parent?.path || ""}/${newName}.md`;
await plugin.moveFile(file, newPath);
actionResults.push(`✅ Renamed ${file.name} to ${newName}.md`);
} else {
actionResults.push(`ℹ️ No name suggestions for ${file.name}`);
}
break;
}
}
} catch (error) {
actionResults.push(`❌ Error processing ${filePath}: ${error.message}`);
}
}

setResults(actionResults);
setIsDone(true);
handleAddResult(JSON.stringify({ success: true, actionResults }));
} catch (error) {
setResults([`❌ Error: ${error.message}`]);
setIsDone(true);
handleAddResult(JSON.stringify({ success: false, error: error.message }));
} finally {
setIsProcessing(false);
}
};

return (
<div className="flex flex-col space-y-4 p-4 border border-[--background-modifier-border] rounded-md">
<div className="text-[--text-normal]">
Ready to process {toolInvocation.args.filePaths.length} file(s) based on content analysis
</div>
{results.length > 0 && (
<div className="text-sm space-y-1">
{results.map((result, i) => (
<div
key={i}
className={
result.startsWith("✅")
? "text-[--text-success]"
: result.startsWith("ℹ️")
? "text-[--text-muted]"
: "text-[--text-error]"
}
>
{result}
</div>
))}
</div>
)}
{!isDone && (
<button
className="px-4 py-2 bg-[--interactive-accent] text-[--text-on-accent] rounded-md hover:bg-[--interactive-accent-hover] disabled:opacity-50"
onClick={handleExecute}
disabled={isProcessing}
>
{isProcessing ? "Processing..." : "Execute Actions"}
</button>
)}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { OnboardHandler } from "./onboard-handler";
import { MoveFilesHandler } from "./move-files-handler";
import { RenameFilesHandler } from "./rename-files-handler";
import { SearchRenameHandler } from "./search-rename-handler";
import { ExecuteActionsHandler } from "./execute-actions-handler";

interface ToolInvocationHandlerProps {
toolInvocation: any;
Expand Down Expand Up @@ -43,6 +44,7 @@ function ToolInvocationHandler({
moveFiles: "Moving Files",
renameFiles: "Renaming Files",
searchByName: "Search Files by Name",
executeActionsOnFileBasedOnPrompt: "Execute Actions on Files",
};
return toolTitles[toolName] || "Tool Invocation";
};
Expand Down Expand Up @@ -123,6 +125,13 @@ function ToolInvocationHandler({
app={app}
/>
),
executeActionsOnFileBasedOnPrompt: () => (
<ExecuteActionsHandler
toolInvocation={toolInvocation}
handleAddResult={handleAddResult}
app={app}
/>
),
};

return handlers[toolInvocation.toolName]?.() || null;
Expand Down
21 changes: 8 additions & 13 deletions packages/web/.turbo/turbo-build.log
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,20 @@
No config path provided, using default path
Reading config file '/home/ubuntu/repos/file-organizer-2000/packages/web/drizzle.config.ts'
Using 'pg' driver for database querying
[⣷] applying migrations...[⣯] applying migrations...[⣟] applying migrations...[⡿] applying migrations...[⢿] applying migrations...[⣻] applying migrations...[⣽] applying migrations...[⣷] applying migrations...[⣯] applying migrations...[⣟] applying migrations...[⡿] applying migrations...[⢿] applying migrations...[✓] migrations applied successfully!Attention: Next.js now collects completely anonymous telemetry regarding usage.
This information is used to shape Next.js' roadmap and prioritize features.
You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
https://nextjs.org/telemetry

▲ Next.js 14.2.21
[⣷] applying migrations...[⣯] applying migrations...[⣟] applying migrations...[⡿] applying migrations...[⢿] applying migrations...[⣻] applying migrations...[⣽] applying migrations...[⣷] applying migrations...[⣯] applying migrations...[⣟] applying migrations...[⡿] applying migrations...[⢿] applying migrations...[✓] migrations applied successfully! ▲ Next.js 14.2.21

  Creating an optimized production build ...
✓ Compiled successfully
  Skipping linting
[?25l   Checking validity of types .   Checking validity of types .. ⚠ TypeScript project references are not fully supported. Attempting to build in incremental mode.
   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .[?25h   Checking validity of types ✓ Checking validity of types
[?25l   Collecting page data .   Collecting page data ..   Collecting page data ...   Collecting page data .(node:12496) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...   Checking validity of types .   Checking validity of types ..   Checking validity of types ...[?25h   Checking validity of types ✓ Checking validity of types
[?25l   Collecting page data .   Collecting page data ..   Collecting page data ...   Collecting page data .   Collecting page data ..(node:11852) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
[?25h ⚠ Using edge runtime on a page currently disables static generation for that page
[?25l   Collecting page data .[?25h   Collecting page data ✓ Collecting page data
[?25l   Generating static pages (0/52) [ ]   Generating static pages (0/52) [= ]   Generating static pages (9/52) [== ]   Generating static pages (22/52) [=== ]   Generating static pages (30/52) [ ===]   Generating static pages (30/52) [ ==]Redeploy cron job started
[?25l   Generating static pages (0/52) [ ]   Generating static pages (0/52) [= ]   Generating static pages (9/52) [== ]   Generating static pages (22/52) [=== ]   Generating static pages (30/52) [ ===](node:11858) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
   Generating static pages (44/52) [ ==]Redeploy cron job started
Error fetching deployment status: n [Error]: Dynamic server usage: Route /api/deployment/status couldn't be rendered statically because it used `headers`. See more info here: https://nextjs.org/docs/messages/dynamic-server-error
at l (/home/ubuntu/repos/file-organizer-2000/packages/web/.next/server/chunks/4385.js:1:57698)
at u (/home/ubuntu/repos/file-organizer-2000/packages/web/.next/server/chunks/7481.js:9:21260)
Expand All @@ -44,10 +41,8 @@ Error fetching deployment status: n [Error]: Dynamic server usage: Route /api/de
description: "Route /api/deployment/status couldn't be rendered statically because it used `headers`. See more info here: https://nextjs.org/docs/messages/dynamic-server-error",
digest: 'DYNAMIC_SERVER_USAGE'
}
(node:12498) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
   Generating static pages (49/52) [ =]   Generating static pages (50/52) [ ][?25h ✓ Generating static pages (52/52)
[?25l   Finalizing page optimization .[?25l   Collecting build traces .[?25h   Collecting build traces ✓ Collecting build traces
[?25h ✓ Generating static pages (52/52)
[?25l   Finalizing page optimization .[?25l   Collecting build traces .   Finalizing page optimization ..   Collecting build traces ..[?25h   Collecting build traces ✓ Collecting build traces
[?25h   Finalizing page optimization ✓ Finalizing page optimization

Route (app) Size First Load JS
Expand Down
7 changes: 7 additions & 0 deletions packages/web/app/api/(newai)/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,13 @@ export async function POST(req: NextRequest) {
message: z.string().describe("Confirmation message to show user")
}),
},
executeActionsOnFileBasedOnPrompt: {
description: "Analyze file content and apply one of (recommendTags & appendTag), (recommendFolders & moveFile), or (recommendName & moveFile)",
parameters: z.object({
filePaths: z.array(z.string()).describe("List of file paths to analyze"),
userPrompt: z.string().describe("User instructions to decide how to rename or re-tag or re-folder the files")
}),
},
},
onFinish: async ({ usage }) => {
console.log("Token usage:", usage);
Expand Down

0 comments on commit c512809

Please sign in to comment.