Welcome to the final chapter of the FileReadTool tutorial!
In the previous chapter, Media Processing Engine, we did the heavy lifting of reading files, compressing images, and converting PDFs into data the AI can understand.
However, we have a usability problem.
We need a way to separate what the "Engine" consumes from what the "Driver" sees. This is the User Interface Presentation layer.
Think of this tool like a modern car.
If we showed the user exactly what the AI sees, the application would be unusable. The UI Presentation layer acts as the dashboard, converting complex data into clean, human-readable summaries.
Imagine the user says:
"Read
logo.pngandserver.log."
Without UI Presentation:
The chat window fills with garbage text: IVBORw0KGgoAAAANSUhEUgAAA... for pages and pages.
With UI Presentation: The user sees:
๐ Read
logo.png
โ Read image (42 KB)
>
๐ Read
server.log
โ Read 150 lines
The AI still gets the full file content silently in the background, but the user keeps a clean workspace.
To achieve this, our system allows us to define two different messages for every tool action:
We use a file called UI.tsx to control the visual side.
When the AI decides to use a tool, the UI updates in two distinct stages: Intent (I am about to do this) and Result (I finished doing this).
When the AI calls the tool, we want to show the user which file is being accessed. We use renderToolUseMessage.
We don't just show the path string; we use a helper <FilePathLink> to make it clickable!
// File: UI.tsx (Simplified)
export function renderToolUseMessage(input, { verbose }) {
const { file_path } = input;
// 1. Get a pretty version of the path (e.g., truncate long paths)
const displayPath = getDisplayPath(file_path);
// 2. Render a clickable link
return (
<FilePathLink filePath={file_path}>
{displayPath}
</FilePathLink>
);
}
Explanation:
This code grabs the file_path from the input (defined in Tool Definition & Interface) and wraps it in a React component. Now, if the user hovers over the filename in the chat, they can see exactly where it is.
This is the most important part. Once the tool finishes, we receive the Output object (the one with the type discriminator we created in Content Type Dispatcher).
We switch on that type to decide what summary to show.
For text files, the most useful metric is the Line Count.
// File: UI.tsx
case 'text': {
const { numLines } = output.file;
return (
<MessageResponse height={1}>
<Text>
Read <Text bold>{numLines}</Text>{' '}
{numLines === 1 ? 'line' : 'lines'}
</Text>
</MessageResponse>
);
}
Explanation:
Instead of dumping the text, we look at numLines. The user sees: "Read 50 lines".
For images, line count makes no sense. We show the File Size.
// File: UI.tsx
case 'image': {
const { originalSize } = output.file;
const formattedSize = formatFileSize(originalSize);
return (
<MessageResponse height={1}>
<Text>Read image ({formattedSize})</Text>
</MessageResponse>
);
}
Explanation:
The variable originalSize comes from the image object created in Media Processing Engine. The user sees: "Read image (2.5 MB)".
Jupyter notebooks are split into "cells." That is the metric that matters to a data scientist.
// File: UI.tsx
case 'notebook': {
const { cells } = output.file;
return (
<MessageResponse height={1}>
<Text>
Read <Text bold>{cells.length}</Text> cells
</Text>
</MessageResponse>
);
}
Sometimes things go wrong. Maybe the file doesn't exist. We want to show a friendly error, not a stack trace.
// File: UI.tsx
export function renderToolUseErrorMessage(result) {
// Check specifically for our "File not found" marker
if (result.includes(FILE_NOT_FOUND_CWD_NOTE)) {
return (
<MessageResponse>
<Text color="error">File not found</Text>
</MessageResponse>
);
}
// Fallback for other errors
return <Text color="error">Error reading file</Text>;
}
Explanation: We check the error string. If it matches our specific "Not Found" error, we render a clean red text message.
Congratulations! You have completed the FileReadTool tutorial.
Let's recap the journey of a single file request:
You now understand the architecture of a robust, production-grade AI tool. You moved from raw inputs to processed data, all while keeping the user experience clean and friendly.
End of Tutorial.
Generated by Code IQ