Skip to content

Commit 5a8f674

Browse files
committed
Update UX to visualize calculation better
1 parent aad3986 commit 5a8f674

File tree

3 files changed

+143
-30
lines changed

3 files changed

+143
-30
lines changed

src/App.jsx

Lines changed: 120 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
InputLabel,
1212
FormControl,
1313
IconButton,
14-
Grid,
14+
Grid2 as Grid,
1515
Accordion,
1616
AccordionSummary,
1717
AccordionDetails,
@@ -73,7 +73,7 @@ function App() {
7373
Azure OpenAI Image Token Calculator
7474
</Typography>
7575
<Grid container spacing={4}>
76-
<Grid item xs={12} md={8}>
76+
<Grid size={{ xs: 12, md: 8 }}>
7777
<form onSubmit={handleSubmit}>
7878
<FormControl fullWidth margin="normal" required>
7979
<InputLabel id="model-label">Model</InputLabel>
@@ -96,7 +96,7 @@ function App() {
9696

9797
{images.map((image, index) => (
9898
<Grid container spacing={1} alignItems="center" key={index}>
99-
<Grid item sm={4}>
99+
<Grid size={{ sm: 4 }}>
100100
<TextField
101101
label="Image Height (px)"
102102
type="number"
@@ -109,7 +109,7 @@ function App() {
109109
fullWidth
110110
/>
111111
</Grid>
112-
<Grid item sm={4}>
112+
<Grid size={{ sm: 4 }}>
113113
<TextField
114114
label="Image Width (px)"
115115
type="number"
@@ -122,7 +122,7 @@ function App() {
122122
fullWidth
123123
/>
124124
</Grid>
125-
<Grid item sm={2}>
125+
<Grid size={{ sm: 2 }}>
126126
<TextField
127127
label="Count"
128128
type="number"
@@ -135,15 +135,15 @@ function App() {
135135
fullWidth
136136
/>
137137
</Grid>
138-
<Grid item sm={1}>
138+
<Grid size={{ sm: 1 }}>
139139
<IconButton
140140
onClick={() => cloneImage(index)}
141141
color="primary"
142142
>
143143
<FileCopy />
144144
</IconButton>
145145
</Grid>
146-
<Grid item sm={1}>
146+
<Grid size={{ sm: 1 }}>
147147
<IconButton
148148
onClick={() => removeImage(index)}
149149
color="secondary"
@@ -175,19 +175,115 @@ function App() {
175175
</form>
176176

177177
<Box mt={2}>
178-
{totalTokens !== null && (
179-
<Typography gutterBottom>
180-
Token Estimate: {totalTokens}
181-
</Typography>
178+
{images.map((image, index) =>
179+
image.resizedHeight ? (
180+
<Box key={index} sx={{ display: "flex" }} mb={2}>
181+
<Box sx={{ flex: "1 0 auto" }}>
182+
<Typography variant="h6" gutterBottom>
183+
Image {index + 1}
184+
</Typography>
185+
<Box
186+
sx={{
187+
display: "flex",
188+
alignItems: "center",
189+
}}
190+
>
191+
<Box sx={{ flexGrow: 1 }}>
192+
<Typography variant="body2" color="textSecondary">
193+
Resized Size
194+
</Typography>
195+
<Typography variant="body1" gutterBottom>
196+
{image.resizedHeight} x {image.resizedWidth}
197+
</Typography>
198+
</Box>
199+
<Box sx={{ flexGrow: 1 }}>
200+
<Typography variant="body2" color="textSecondary">
201+
Tiles (per image)
202+
</Typography>
203+
<Typography variant="body1" gutterBottom>
204+
{image.tilesHigh} x {image.tilesWide}
205+
</Typography>
206+
</Box>
207+
<Box sx={{ flexGrow: 1 }}>
208+
<Typography variant="body2" color="textSecondary">
209+
Total tiles
210+
</Typography>
211+
<Typography variant="body1" gutterBottom>
212+
{image.totalTiles}
213+
</Typography>
214+
</Box>
215+
</Box>
216+
</Box>
217+
</Box>
218+
) : null
182219
)}
183-
{totalCost !== null && (
184-
<Typography gutterBottom>
185-
Cost Estimate: ${totalCost}
186-
</Typography>
220+
221+
{totalTokens !== null && (
222+
<Box sx={{ display: "flex" }} mb={2}>
223+
<Box sx={{ flex: "1 0 auto" }}>
224+
<Typography variant="h6" gutterBottom>
225+
Result
226+
</Typography>
227+
<Box
228+
sx={{
229+
display: "flex",
230+
alignItems: "center",
231+
}}
232+
>
233+
{model && (
234+
<Box sx={{ flexGrow: 1 }}>
235+
<Typography variant="body2" color="textSecondary">
236+
Base tokens
237+
</Typography>
238+
<Typography variant="body1" gutterBottom>
239+
{model.baseTokens}
240+
</Typography>
241+
</Box>
242+
)}
243+
244+
{model && (
245+
<Box sx={{ flexGrow: 1 }}>
246+
<Typography variant="body2" color="textSecondary">
247+
Tile tokens
248+
</Typography>
249+
<Typography variant="body1" gutterBottom>
250+
{model.tokensPerTile} x{" "}
251+
{images
252+
.map((image) => image.totalTiles ?? 0)
253+
.reduce((acc, val) => acc + val, 0)}{" "}
254+
= {totalTokens - model.baseTokens}
255+
</Typography>
256+
</Box>
257+
)}
258+
259+
{totalTokens !== null && (
260+
<Box sx={{ flexGrow: 1 }}>
261+
<Typography variant="body2" color="textSecondary">
262+
Total tokens
263+
</Typography>
264+
<Typography variant="body1" gutterBottom>
265+
{totalTokens}
266+
</Typography>
267+
</Box>
268+
)}
269+
270+
{totalCost !== null && (
271+
<Box sx={{ flexGrow: 1 }}>
272+
<Typography variant="body2" color="textSecondary">
273+
Total cost
274+
</Typography>
275+
<Typography variant="body1" gutterBottom>
276+
${totalCost}
277+
</Typography>
278+
</Box>
279+
)}
280+
</Box>
281+
</Box>
282+
</Box>
187283
)}
188284
</Box>
189285
</Grid>
190-
<Grid item xs={12} md={4}>
286+
<Grid size={{ xs: 12, md: 4 }}>
191287
<Accordion expanded>
192288
<AccordionSummary
193289
aria-controls="calculation-explanation-content"
@@ -197,35 +293,35 @@ function App() {
197293
</AccordionSummary>
198294
{model ? (
199295
<AccordionDetails>
200-
<Typography paragraph>
201-
The calculation involves several steps:
296+
<Typography>
297+
<p>The calculation involves several steps:</p>
202298
</Typography>
203-
<Typography paragraph>
299+
<Typography>
204300
1. <b>Resizing Images</b>: Ensure each image is resized to
205301
fit within the maximum dimension {model.maxImageDimension}
206302
px, and has at least {model.imageMinSizeLength}px on the
207303
shortest side, while maintaining its aspect ratio.
208304
</Typography>
209-
<Typography paragraph>
305+
<Typography>
210306
2. <b>Calculating Tiles</b>: The resized image is divided
211307
into tiles based on the model's tile size of{" "}
212308
{model.tileSizeLength}px by {model.tileSizeLength}px.
213309
</Typography>
214-
<Typography paragraph>
310+
<Typography>
215311
3. <b>Token Calculation</b>: The total number of tokens is
216312
calculated by multiplying the number of tiles by the tokens
217313
per tile ({model.tokensPerTile}) and adding an additional
218-
buffer of {model.additionalBuffer} tokens.
314+
buffer of {model.baseTokens} tokens.
219315
</Typography>
220-
<Typography paragraph>
316+
<Typography>
221317
4. <b>Cost Calculation</b>: The total cost is calculated
222318
based on the total number of tokens and the cost per
223319
thousand tokens (${model.costPerThousandTokens}).
224320
</Typography>
225321
</AccordionDetails>
226322
) : (
227323
<AccordionDetails>
228-
<Typography paragraph>
324+
<Typography>
229325
Please select a model to see the calculation explanation.
230326
</Typography>
231327
</AccordionDetails>

src/stores/CalcStore.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ export const calcStore = (set, get) => ({
4242
resizedHeight = resizedHeight * scaleFactor;
4343
}
4444

45+
resizedHeight = Math.round(resizedHeight);
46+
resizedWidth = Math.round(resizedWidth);
47+
4548
return { height: resizedHeight, width: resizedWidth };
4649
}
4750

@@ -56,7 +59,7 @@ export const calcStore = (set, get) => ({
5659
const maxImageDimension = model.maxImageDimension;
5760
const imageMinSizeLength = model.imageMinSizeLength;
5861
const tileSizeLength = model.tileSizeLength;
59-
const additionalBuffer = model.additionalBuffer;
62+
const baseTokens = model.baseTokens;
6063
const costPerThousandTokens = model.costPerThousandTokens;
6164

6265
const imageTileCount = images.flatMap((image) => {
@@ -66,20 +69,34 @@ export const calcStore = (set, get) => ({
6669
image.height,
6770
image.width
6871
);
72+
73+
image.resizedHeight = imgSize.height;
74+
image.resizedWidth = imgSize.width;
75+
6976
const imageTiles = getImageTileCount(
7077
tileSizeLength,
7178
imgSize.height,
7279
imgSize.width
7380
);
81+
82+
image.tilesHigh = imageTiles.tilesHigh;
83+
image.tilesWide = imageTiles.tilesWide;
84+
7485
const multiplier = image.multiplier;
86+
87+
image.totalTiles =
88+
imageTiles.tilesHigh * imageTiles.tilesWide * multiplier;
89+
7590
return Array.from({ length: multiplier }, () => imageTiles);
7691
});
7792

93+
set(() => ({ images: images }));
94+
7895
const totalTokens =
7996
imageTileCount.reduce(
8097
(acc, tiles) => acc + tiles.tilesHigh * tiles.tilesWide * tokensPerTile,
8198
0
82-
) + additionalBuffer;
99+
) + baseTokens;
83100

84101
set(() => ({ totalTokens: totalTokens }));
85102

src/stores/ModelStore.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const modelStore = (set, get) => ({
66
maxImageDimension: 2048,
77
imageMinSizeLength: 768,
88
tileSizeLength: 512,
9-
additionalBuffer: 85,
9+
baseTokens: 85,
1010
costPerThousandTokens: 0.00275,
1111
},
1212
{
@@ -15,7 +15,7 @@ export const modelStore = (set, get) => ({
1515
maxImageDimension: 2048,
1616
imageMinSizeLength: 768,
1717
tileSizeLength: 512,
18-
additionalBuffer: 85,
18+
baseTokens: 85,
1919
costPerThousandTokens: 0.0025,
2020
},
2121
{
@@ -24,7 +24,7 @@ export const modelStore = (set, get) => ({
2424
maxImageDimension: 2048,
2525
imageMinSizeLength: 768,
2626
tileSizeLength: 512,
27-
additionalBuffer: 2833,
27+
baseTokens: 2833,
2828
costPerThousandTokens: 0.000165,
2929
},
3030
{
@@ -33,7 +33,7 @@ export const modelStore = (set, get) => ({
3333
maxImageDimension: 2048,
3434
imageMinSizeLength: 768,
3535
tileSizeLength: 512,
36-
additionalBuffer: 85,
36+
baseTokens: 85,
3737
costPerThousandTokens: 0.005,
3838
},
3939
],

0 commit comments

Comments
 (0)