Skip to content

Commit 20ae790

Browse files
committed
Merge branch 'feature/avg-cost-per-page' into 'develop'
feat: add avg cost per page to test results, comparison, and downloads See merge request genaiic-reusable-assets/engagement-artifacts/genaiic-idp-accelerator!606
2 parents 77533f0 + 58b761f commit 20ae790

6 files changed

Lines changed: 56 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ SPDX-License-Identifier: MIT-0
77

88
### Added
99

10+
- **Average Cost Per Page Metric** — Test results and test comparison views now display an "Avg Cost/Page" metric, calculated from total cost and page counts in the cost breakdown. Also included in CSV and JSON exports from the comparison view.
11+
12+
- **Prompt Preview** — New "Prompt Preview" tab in the Configuration page lets you preview the actual prompts sent to the LLM for each processing step (Classification, Extraction, Assessment, Summarization). Config-derived placeholders are filled in with real values (class names, cleaned JSON Schema), while document-specific placeholders are shown as highlighted markers. Includes token estimates, copy-to-clipboard, and a substitution details panel showing the exact schema sent to the LLM. Helps optimize document class schemas and prompt templates.
13+
14+
- **IDP CLI `chat` Command & SDK `ChatOperation`** — Interactive Agent Companion Chat from the terminal and programmatic SDK access. Runs the same multi-agent orchestrator as the Web UI locally, with real-time streaming and multi-turn conversation support. Includes Analytics Agent, Error Analyzer Agent, and optionally Code Intelligence Agent (`--enable-code-intelligence`). Available as `idp-cli chat --stack-name <stack>` for interactive use, `--prompt` flag for single-shot scripting, and `client.chat.send_message()` in the Python SDK. See `docs/idp-cli.md#chat`.
15+
1016
- **Multi-Document Discovery** — New capability to automatically discover document classes from a collection of documents. Instead of manually defining document schemas one at a time, users point to a folder of mixed documents and the system automatically identifies document types, clusters similar documents, generates JSON Schemas with field definitions for each type, and saves them to a configuration version — ready for immediate use in the processing pipeline. Available from the Web UI, CLI (`idp-cli discover-multidoc`), and SDK (`client.discovery.run_multi_doc()`).
1117
- **Web UI**: New "Multi-Document" tab on the Discovery page with job submission form (config version selector, bucket selector, S3 prefix input, zip upload), jobs table with search/filter/sort/pagination, and detailed job results page with pipeline progress, expandable JSON schemas, config deep-links, and Quality Review Report
1218
- **CLI**: `idp-cli discover-multidoc --dir ./samples/ -o ./schemas/` with Rich progress bars, results table, and reflection report

docs/test-studio.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ For full details on configuration versioning, see [configuration-versions.md](co
484484
- Comprehensive metrics display including:
485485
- **Test run metadata**: Configuration version, duration, context, file counts
486486
- **Overall accuracy and confidence metrics**
487+
- **Cost metrics**: Total cost and average cost per page
487488
- **Accuracy breakdown** (precision, recall, F1-score, false alarm rate, false discovery rate)
488489
- **Field-Level Metrics**: Per-field extraction performance table with columns: Field Name, Accuracy, Precision, Recall, TP, FP, TN, FN
489490
- **Average Document Split Classification Metrics**:

src/ui/src/components/test-studio/TestComparison.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ import TestStudioHeader from './TestStudioHeader';
2020
import useLocalStorage from '../common/local-storage';
2121
import useConfigurationVersions from '../../hooks/use-configuration-versions';
2222
import { formatConfigVersionLink, formatConfigVersionText, type ConfigVersion as UtilsConfigVersion } from './utils/configVersionUtils';
23-
import { parseComparisonMetrics, parseWeightedOverallScores, parseConfigSettingValues } from '../../graphql/awsjson-parsers';
23+
import {
24+
parseComparisonMetrics,
25+
parseWeightedOverallScores,
26+
parseConfigSettingValues,
27+
calculateAvgCostPerPage,
28+
} from '../../graphql/awsjson-parsers';
29+
import type { CostBreakdown } from '../../graphql/awsjson-types';
2430

2531
const client = generateClient();
2632

@@ -302,6 +308,13 @@ const TestComparison = ({ preSelectedTestRunIds = [] }: TestComparisonProps): Re
302308
run.totalCost !== null && run.totalCost !== undefined ? `$${Number(run.totalCost).toFixed(4)}` : 'N/A',
303309
),
304310
],
311+
[
312+
'Avg Cost/Page',
313+
...Object.values(completeTestRuns).map((run) => {
314+
const avg = calculateAvgCostPerPage(run.totalCost as number, run.costBreakdown as CostBreakdown);
315+
return avg !== null ? `$${avg.toFixed(4)}` : 'N/A';
316+
}),
317+
],
305318
[
306319
'Average Accuracy',
307320
...Object.values(completeTestRuns).map((run) =>
@@ -624,6 +637,7 @@ const TestComparison = ({ preSelectedTestRunIds = [] }: TestComparisonProps): Re
624637
completedFiles: testRun.completedFiles,
625638
failedFiles: testRun.failedFiles,
626639
totalCost: testRun.totalCost,
640+
avgCostPerPage: calculateAvgCostPerPage(testRun.totalCost as number, testRun.costBreakdown as CostBreakdown),
627641
averageAccuracy: testRun.overallAccuracy,
628642
averageConfidence: testRun.averageConfidence,
629643
averageWeightedOverallScore: (() => {
@@ -818,6 +832,15 @@ const TestComparison = ({ preSelectedTestRunIds = [] }: TestComparisonProps): Re
818832
]),
819833
),
820834
},
835+
{
836+
metric: 'Avg Cost/Page',
837+
...Object.fromEntries(
838+
Object.entries(completeTestRuns).map(([testRunId, testRun]) => {
839+
const avg = calculateAvgCostPerPage(testRun.totalCost as number, testRun.costBreakdown as CostBreakdown);
840+
return [testRunId, avg !== null ? `$${avg.toFixed(4)}` : 'N/A'];
841+
}),
842+
),
843+
},
821844
{
822845
metric: 'Average Accuracy',
823846
...Object.fromEntries(

src/ui/src/components/test-studio/TestResults.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import useAppContext from '../../contexts/app';
3535
import { formatConfigVersionLink } from './utils/configVersionUtils';
3636
import {
3737
parseCostBreakdown,
38+
calculateAvgCostPerPage,
3839
parseAccuracyBreakdown,
3940
parseSplitClassificationMetrics,
4041
parseFieldMetrics,
@@ -751,6 +752,9 @@ const TestResults = ({ testRunId, setSelectedTestRunId }: TestResultsProps): Rea
751752

752753
// eslint-disable-next-line @typescript-eslint/no-explicit-any
753754
const costBreakdown: any = results.costBreakdown ? parseCostBreakdown(results.costBreakdown as string) : null;
755+
756+
// Calculate avg cost per page from costBreakdown page counts
757+
const avgCostPerPage = calculateAvgCostPerPage(results.totalCost as number, costBreakdown);
754758
// eslint-disable-next-line @typescript-eslint/no-explicit-any
755759
const accuracyBreakdown: any = results.accuracyBreakdown ? parseAccuracyBreakdown(results.accuracyBreakdown as string) : null;
756760
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1012,6 +1016,10 @@ const TestResults = ({ testRunId, setSelectedTestRunId }: TestResultsProps): Rea
10121016
{results.totalCost !== null && results.totalCost !== undefined ? `$${(results.totalCost as number).toFixed(4)}` : 'N/A'}
10131017
</Box>
10141018
</Box>
1019+
<Box>
1020+
<Box variant="awsui-key-label">Avg Cost/Page</Box>
1021+
<Box fontSize="heading-l">{avgCostPerPage !== null ? `$${avgCostPerPage.toFixed(4)}` : 'N/A'}</Box>
1022+
</Box>
10151023
<Box>
10161024
<Box variant="awsui-key-label">Avg Confidence</Box>
10171025
<Box fontSize="heading-l">

src/ui/src/graphql/awsjson-parsers.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,22 @@ export function parseCostBreakdown(json: unknown): CostBreakdown {
5858
return safeParse<CostBreakdown>(json, {});
5959
}
6060

61+
export function calculateTotalPages(costBreakdown: CostBreakdown): number {
62+
let totalPages = 0;
63+
Object.values(costBreakdown).forEach((services) => {
64+
Object.values(services).forEach((details) => {
65+
if (details.unit === 'pages') totalPages += Number(details.value) || 0;
66+
});
67+
});
68+
return totalPages;
69+
}
70+
71+
export function calculateAvgCostPerPage(totalCost: number | null | undefined, costBreakdown: CostBreakdown | null): number | null {
72+
if (totalCost == null || !costBreakdown) return null;
73+
const totalPages = calculateTotalPages(costBreakdown);
74+
return totalPages > 0 ? totalCost / totalPages : null;
75+
}
76+
6177
export function parseTestRunConfig(json: unknown): TestRunConfig | null {
6278
return safeParse<TestRunConfig | null>(json, null);
6379
}

src/ui/src/graphql/awsjson-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export interface CostBreakdownServiceDetail {
3030
estimated_cost?: number;
3131
value?: number;
3232
unit_cost?: number;
33+
unit?: string;
3334
[key: string]: unknown;
3435
}
3536

0 commit comments

Comments
 (0)