Skip to content

Commit caa283a

Browse files
mrjasonroyclaude
andcommitted
fix: improve file-generator tool with presigned URLs and system prompts
- Add explicit logging to track presigned URL generation - Remove fallback to sourceUrl to ensure presigned URLs are always used - Add file generation capability to system prompts for all models - Update tool description to prevent LLMs from including raw URLs in responses - Add guide field to tool result for better user messaging This ensures all file downloads work correctly with private S3 buckets and improves model awareness of file generation capabilities. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 8ee540b commit caa283a

File tree

2 files changed

+37
-6
lines changed

2 files changed

+37
-6
lines changed

src/lib/ai/prompts.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ You can assist with:
9797
- Analysis and problem-solving across various domains
9898
- Using available tools and resources to complete tasks
9999
- Adapting communication to user preferences and context
100+
- Generating downloadable files (CSV, JSON, PDF, code files, etc.) when users request data exports or file creation
100101
</general_capabilities>`;
101102

102103
// Communication preferences
@@ -124,8 +125,9 @@ ${userPreferences.responseStyleExample}
124125
prompt += `
125126
126127
- When using tools, briefly mention which tool you'll use with natural phrases
127-
- Examples: "I'll search for that information", "Let me check the weather", "I'll run some calculations"
128+
- Examples: "I'll search for that information", "Let me check the weather", "I'll run some calculations", "I'll generate that file for you"
128129
- Use \`mermaid\` code blocks for diagrams and charts when helpful
130+
- When users request data exports, file downloads, or structured data files, use the file-generator tool to create downloadable files
129131
</communication_preferences>`;
130132
}
131133

@@ -178,6 +180,7 @@ ${userInfo.join("\n")}
178180
You excel at conversational voice interactions by:
179181
- Providing clear, natural spoken responses
180182
- Using available tools to gather information and complete tasks
183+
- Generating downloadable files when users request data exports or file creation
181184
- Adapting communication to user preferences and context
182185
</voice_capabilities>`;
183186

src/lib/ai/tools/file-generator/index.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export type FileGeneratorToolResult = {
1212
size: number;
1313
}[];
1414
description?: string;
15+
guide?: string;
1516
};
1617

1718
export const fileGeneratorTool = createTool({
@@ -22,7 +23,9 @@ export const fileGeneratorTool = createTool({
2223
- Code files: Python, JavaScript, HTML, CSS, etc.
2324
- Structured data exports
2425
25-
The tool will generate the file, upload it to storage, and provide a download link. Do not use this for images (use image-manager instead) or for simple text responses that don't need to be downloaded.`,
26+
The tool will generate the file, upload it to storage, and provide a download link. Do not use this for images (use image-manager instead) or for simple text responses that don't need to be downloaded.
27+
28+
IMPORTANT: After invoking this tool, do NOT include file URLs in your text response. The download links are automatically displayed in the UI above your message. Simply acknowledge that the file(s) have been generated.`,
2629
inputSchema: z.object({
2730
files: z
2831
.array(
@@ -89,23 +92,48 @@ The tool will generate the file, upload it to storage, and provide a download li
8992
contentType: mimeType,
9093
});
9194

95+
logger.info("File uploaded:", {
96+
key: uploaded.key,
97+
sourceUrl: uploaded.sourceUrl,
98+
hasGetDownloadUrl: !!serverFileStorage.getDownloadUrl,
99+
});
100+
92101
// Get presigned URL for private buckets if available
93-
const downloadUrl = serverFileStorage.getDownloadUrl
94-
? await serverFileStorage.getDownloadUrl(uploaded.key)
95-
: uploaded.sourceUrl;
102+
let downloadUrl: string;
103+
if (serverFileStorage.getDownloadUrl) {
104+
downloadUrl = await serverFileStorage.getDownloadUrl(uploaded.key);
105+
logger.info("Presigned URL generated:", {
106+
downloadUrl,
107+
isPresigned: downloadUrl.includes("X-Amz"),
108+
});
109+
} else {
110+
downloadUrl = uploaded.sourceUrl;
111+
logger.info("Using source URL (no getDownloadUrl):", {
112+
downloadUrl,
113+
});
114+
}
96115

97116
return {
98-
url: downloadUrl || uploaded.sourceUrl,
117+
url: downloadUrl,
99118
filename: uploaded.metadata.filename || file.filename,
100119
mimeType: uploaded.metadata.contentType || mimeType,
101120
size: uploaded.metadata.size || buffer.length,
102121
};
103122
}),
104123
);
105124

125+
const fileCount = uploadedFiles.length;
126+
const guide =
127+
fileCount > 0
128+
? fileCount === 1
129+
? "Your file has been generated successfully and is ready for download above. You can click the download button to save it to your device."
130+
: `Your ${fileCount} files have been generated successfully and are ready for download above. You can click the download buttons to save them to your device.`
131+
: undefined;
132+
106133
return {
107134
files: uploadedFiles,
108135
description,
136+
guide,
109137
};
110138
} catch (e) {
111139
logger.error(e);

0 commit comments

Comments
 (0)