Skip to content

Commit 4baa426

Browse files
committed
fix: address review feedback for LLM provider pricing
- Split LlmProviderPricingDialog into smaller components - Use data-cy from tab items instead of hardcoded selector - Use llm-providers-server data-cy in e2e tests - Clarify "Tokens" column as "Tokens (per string)" - Fix BigMeta instruction wording in CLAUDE.md
1 parent 3580129 commit 4baa426

7 files changed

Lines changed: 223 additions & 224 deletions

File tree

e2e/cypress/e2e/llmProviders/llmProviders.cy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ describe('basic prompt', () => {
9292
});
9393

9494
it('server providers visible', () => {
95-
gcy('organization-llm-providers-tab').contains('Server').click();
95+
gcy('llm-providers-server').click();
9696
gcy('llm-provider-item-name').should('contain', 'server-provider');
9797
gcyAdvanced({
9898
value: 'llm-provider-item-type',
@@ -101,7 +101,7 @@ describe('basic prompt', () => {
101101
});
102102

103103
it('does not show pricing info when billing is disabled', () => {
104-
gcy('organization-llm-providers-tab').contains('Server').click();
104+
gcy('llm-providers-server').click();
105105
gcy('llm-provider-item-name').should('contain', 'server-provider');
106106
gcy('llm-provider-pricing-info').should('not.exist');
107107
});

e2e/cypress/support/dataCyType.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,6 @@ declare namespace DataCy {
569569
"organization-invitation-cancel-button" |
570570
"organization-invitation-copy-button" |
571571
"organization-invitation-item" |
572-
"organization-llm-providers-tab" |
573572
"organization-member-item" |
574573
"organization-member-leave-button" |
575574
"organization-members-remove-user-button" |

webapp/CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Tolgee REST API — the running app will pick them up automatically.
1313
context upload, tagging) with the full details (key names, translations, tags, etc.). Ask the user for confirmation
1414
once, then execute all calls together.
1515

16-
**Important:** Remember to upload the context (BigMeta) and Screenshots for each key.
16+
**Important:** Always upload screenshots for each key. Upload BigMeta context when at least 2 related keys are present.
1717

1818
**Important:** Always provide `defaultValue` when using the `T` component or `t()` function. This ensures the UI
1919
displays meaningful text before translations are uploaded to Tolgee, which is needed for screenshots to show correct
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import {
2+
Table,
3+
TableBody,
4+
TableCell,
5+
TableHead,
6+
TableRow,
7+
Typography,
8+
} from '@mui/material';
9+
import { T } from '@tolgee/react';
10+
11+
const INPUT_TOKENS_CONTEXT_AND_SCREENSHOTS = 1100;
12+
const INPUT_TOKENS_CONTEXT_NO_SCREENSHOTS = 700;
13+
const INPUT_TOKENS_NO_CONTEXT = 260;
14+
const OUTPUT_TOKENS = 100;
15+
const STRINGS_COUNT = 1000;
16+
17+
type Props = {
18+
inputPrice: number;
19+
outputPrice: number;
20+
pricePerMtCredit: number | null;
21+
formatCredits: (value: number) => string;
22+
creditsToEur: (credits: number) => string | null;
23+
};
24+
25+
function estimateCredits(
26+
inputPrice: number,
27+
outputPrice: number,
28+
inputTokensPerString: number,
29+
outputTokensPerString: number
30+
): number {
31+
return (
32+
(inputTokensPerString * inputPrice + outputTokensPerString * outputPrice) *
33+
STRINGS_COUNT
34+
);
35+
}
36+
37+
const ESTIMATE_ROWS = [
38+
{
39+
dataCy: 'llm-provider-pricing-estimate-context-and-screenshots',
40+
keyName: 'llm_provider_pricing_context_and_screenshots',
41+
defaultValue: 'Context + screenshots',
42+
inputTokens: INPUT_TOKENS_CONTEXT_AND_SCREENSHOTS,
43+
},
44+
{
45+
dataCy: 'llm-provider-pricing-estimate-context-no-screenshots',
46+
keyName: 'llm_provider_pricing_context_no_screenshots',
47+
defaultValue: 'Context, no screenshots',
48+
inputTokens: INPUT_TOKENS_CONTEXT_NO_SCREENSHOTS,
49+
},
50+
{
51+
dataCy: 'llm-provider-pricing-estimate-no-context',
52+
keyName: 'llm_provider_pricing_no_context',
53+
defaultValue: 'No context',
54+
inputTokens: INPUT_TOKENS_NO_CONTEXT,
55+
},
56+
] as const;
57+
58+
export const EstimateCostTable = ({
59+
inputPrice,
60+
outputPrice,
61+
pricePerMtCredit,
62+
formatCredits,
63+
creditsToEur,
64+
}: Props) => {
65+
return (
66+
<>
67+
<Typography variant="subtitle2" gutterBottom>
68+
<T
69+
keyName="llm_provider_pricing_estimate_title"
70+
defaultValue="Estimated cost for 1,000 strings"
71+
/>
72+
</Typography>
73+
<Typography variant="body2" color="text.secondary" gutterBottom>
74+
<T
75+
keyName="llm_provider_pricing_estimate_description"
76+
defaultValue="Estimated costs for translating 1,000 strings. Context includes key name, description, glossary terms, and translation memory. Screenshots add visual context."
77+
/>
78+
</Typography>
79+
<Table size="small" data-cy="llm-provider-pricing-estimate-table">
80+
<TableHead>
81+
<TableRow>
82+
<TableCell />
83+
<TableCell align="right">
84+
<T
85+
keyName="llm_provider_pricing_tokens"
86+
defaultValue="Tokens (per string)"
87+
/>
88+
</TableCell>
89+
<TableCell align="right">
90+
<T
91+
keyName="llm_provider_pricing_credits"
92+
defaultValue="Credits"
93+
/>
94+
</TableCell>
95+
{pricePerMtCredit != null && (
96+
<TableCell align="right">
97+
<T keyName="llm_provider_pricing_eur" defaultValue="EUR" />
98+
</TableCell>
99+
)}
100+
</TableRow>
101+
</TableHead>
102+
<TableBody>
103+
{ESTIMATE_ROWS.map((row) => {
104+
const totalTokens = row.inputTokens + OUTPUT_TOKENS;
105+
const credits = estimateCredits(
106+
inputPrice,
107+
outputPrice,
108+
row.inputTokens,
109+
OUTPUT_TOKENS
110+
);
111+
return (
112+
<TableRow key={row.dataCy} data-cy={row.dataCy}>
113+
<TableCell>
114+
<T keyName={row.keyName} defaultValue={row.defaultValue} />
115+
</TableCell>
116+
<TableCell align="right">
117+
{'~'}
118+
{formatCredits(totalTokens)}
119+
</TableCell>
120+
<TableCell align="right">
121+
{'~'}
122+
{formatCredits(credits)}
123+
</TableCell>
124+
{pricePerMtCredit != null && (
125+
<TableCell align="right">
126+
{'~'}
127+
{creditsToEur(credits)}
128+
</TableCell>
129+
)}
130+
</TableRow>
131+
);
132+
})}
133+
</TableBody>
134+
</Table>
135+
</>
136+
);
137+
};

0 commit comments

Comments
 (0)