Skip to content

Commit 26e1835

Browse files
author
Bob Strahan
committed
Cost Estimator UI Feature for Context Grouping and Subtotals
1 parent dc9a1c4 commit 26e1835

File tree

8 files changed

+43
-13
lines changed

8 files changed

+43
-13
lines changed

CHANGELOG.md

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

88
### Added
99

10+
- **Cost Estimator UI Feature for Context Grouping and Subtotals**
11+
- Added context grouping functionality to organize cost estimates by logical categories (e.g. OCR, Classification, etc.)
12+
- Implemented subtotal calculations for better cost breakdown visualization
13+
1014
- **DynamoDB Caching for Resilient Classification**
1115
- Added optional DynamoDB caching to the multimodal page-level classification service to improve efficiency and resilience
1216
- Cache successful page classification results to avoid redundant processing during retries when some pages fail due to throttling

lib/idp_common_pkg/idp_common/bedrock/client.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ def __call__(
7878
top_k: Optional[Union[float, str]] = None,
7979
top_p: Optional[Union[float, str]] = None,
8080
max_tokens: Optional[Union[int, str]] = None,
81-
max_retries: Optional[int] = None
81+
max_retries: Optional[int] = None,
82+
context: str = "Unspecified"
8283
) -> Dict[str, Any]:
8384
"""
8485
Make the instance callable with the same signature as the original function.
@@ -109,7 +110,8 @@ def __call__(
109110
top_k=top_k,
110111
top_p=top_p,
111112
max_tokens=max_tokens,
112-
max_retries=effective_max_retries
113+
max_retries=effective_max_retries,
114+
context=context
113115
)
114116

115117
def _preprocess_content_for_cachepoint(self, content: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
@@ -177,7 +179,8 @@ def invoke_model(
177179
top_k: Optional[Union[float, str]] = 5,
178180
top_p: Optional[Union[float, str]] = 0.1,
179181
max_tokens: Optional[Union[int, str]] = None,
180-
max_retries: Optional[int] = None
182+
max_retries: Optional[int] = None,
183+
context: str = "Unspecified"
181184
) -> Dict[str, Any]:
182185
"""
183186
Invoke a Bedrock model with retry logic.
@@ -335,7 +338,8 @@ def invoke_model(
335338
converse_params=converse_params,
336339
retry_count=0,
337340
max_retries=effective_max_retries,
338-
request_start_time=request_start_time
341+
request_start_time=request_start_time,
342+
context=context
339343
)
340344

341345
return result
@@ -346,7 +350,8 @@ def _invoke_with_retry(
346350
retry_count: int,
347351
max_retries: int,
348352
request_start_time: float,
349-
last_exception: Exception = None
353+
last_exception: Exception = None,
354+
context: str = "Unspecified"
350355
) -> Dict[str, Any]:
351356
"""
352357
Recursive helper method to handle retries for Bedrock invocation.
@@ -424,7 +429,7 @@ def _invoke_with_retry(
424429
response_with_metering = {
425430
"response": response,
426431
"metering": {
427-
f"bedrock/{converse_params['modelId']}": {
432+
f"{context}/bedrock/{converse_params['modelId']}": {
428433
**usage
429434
}
430435
}
@@ -470,7 +475,8 @@ def _invoke_with_retry(
470475
retry_count=retry_count + 1,
471476
max_retries=max_retries,
472477
request_start_time=request_start_time,
473-
last_exception=e
478+
last_exception=e,
479+
context=context
474480
)
475481
else:
476482
logger.error(f"Non-retryable Bedrock error: {error_code} - {error_message}")
@@ -838,6 +844,7 @@ def _sanitize_response_for_logging(self, response: Dict[str, Any]) -> Dict[str,
838844
top_p: Optional top_p parameter (float or string)
839845
max_tokens: Optional max_tokens parameter (int or string)
840846
max_retries: Optional override for the instance's max_retries setting
847+
context: Context prefix for metering key (default: "Unspecified")
841848
842849
Returns:
843850
Bedrock response object with metering information

lib/idp_common_pkg/idp_common/classification/service.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ def classify_page_sagemaker(
770770

771771
# Add some metering data for consistency with Bedrock
772772
metering = {
773-
"sagemaker/invoke_endpoint": {
773+
"Classification/sagemaker/invoke_endpoint": {
774774
"invocations": 1,
775775
}
776776
}
@@ -910,6 +910,7 @@ def _invoke_bedrock_model(
910910
top_k=config["top_k"],
911911
top_p=config["top_p"],
912912
max_tokens=config["max_tokens"],
913+
context="Classification",
913914
)
914915

915916
def _create_unclassified_result(

lib/idp_common_pkg/idp_common/extraction/service.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,7 @@ def process_document_section(self, document: Document, section_id: str) -> Docum
708708
top_k=top_k,
709709
top_p=top_p,
710710
max_tokens=max_tokens,
711+
context="Extraction",
711712
)
712713

713714
total_duration = time.time() - request_start_time

lib/idp_common_pkg/idp_common/ocr/service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ def _process_single_page(
284284
# Extract metering data
285285
feature_combo = self._feature_combo()
286286
metering = {
287-
f"textract/{self._get_api_name()}{feature_combo}": {
287+
f"OCR/textract/{self._get_api_name()}{feature_combo}": {
288288
"pages": textract_result["DocumentMetadata"]["Pages"]
289289
}
290290
}

lib/idp_common_pkg/idp_common/summarization/service.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ def _invoke_bedrock_model(
221221
top_k=config["top_k"],
222222
top_p=config["top_p"],
223223
max_tokens=config["max_tokens"],
224+
context="Summarization",
224225
)
225226

226227
def _create_error_summary(self, error_message: str) -> DocumentSummary:

patterns/pattern-1/src/processresults_function/index.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,10 +549,10 @@ def handler(event, context):
549549

550550
# Add metering information
551551
document.metering = {
552-
"bda/documents-custom": {
552+
"BDAProject/bda/documents-custom": {
553553
"pages": custom_pages_count
554554
},
555-
"bda/documents-standard": {
555+
"BDAProject/bda/documents-standard": {
556556
"pages": standard_pages_count
557557
}
558558
}

src/ui/src/components/document-panel/DocumentPanel.jsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,24 @@ const MeteringTable = ({ meteringData, preCalculatedTotals }) => {
142142
contextGroups[item.context].push(item);
143143
});
144144

145-
// Sort contexts alphabetically and add items with subtotals
146-
const sortedContexts = Object.keys(contextGroups).sort();
145+
// Sort contexts in specific order: OCR, Classification, Extraction, Summarization
146+
const contextOrder = ['OCR', 'Classification', 'Extraction', 'Summarization'];
147+
const sortedContexts = Object.keys(contextGroups).sort((a, b) => {
148+
const aIndex = contextOrder.indexOf(a);
149+
const bIndex = contextOrder.indexOf(b);
150+
151+
// If both contexts are in the predefined order, sort by their position
152+
if (aIndex !== -1 && bIndex !== -1) {
153+
return aIndex - bIndex;
154+
}
155+
156+
// If only one context is in the predefined order, it comes first
157+
if (aIndex !== -1) return -1;
158+
if (bIndex !== -1) return 1;
159+
160+
// If neither context is in the predefined order, sort alphabetically
161+
return a.localeCompare(b);
162+
});
147163

148164
sortedContexts.forEach((context) => {
149165
// Add all items for this context

0 commit comments

Comments
 (0)