-
Notifications
You must be signed in to change notification settings - Fork 62
Add Execution Cost to Html Reports #243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
f65a79c
0e6aca6
49273a6
5a752cf
aa2cf3c
6e1ea26
b7225e7
a86e5e2
d90a3ac
cdadaad
9b506b8
bac8638
ad10a3e
962fe71
a871a97
47f78b2
a002697
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
| ) | ||
| from askui.models.shared.prompts import SystemPrompt | ||
| from askui.models.shared.tools import ToolCollection | ||
| from askui.utils.model_pricing import ModelPricing | ||
|
|
||
| _DEFAULT_MODEL_ID = "claude-sonnet-4-6" | ||
|
|
||
|
|
@@ -38,6 +39,11 @@ class AnthropicVlmProvider(VlmProvider): | |
| `\"claude-sonnet-4-6\"`. | ||
| client (Anthropic | None, optional): Pre-configured Anthropic client. | ||
| If provided, other connection parameters are ignored. | ||
| input_cost_per_million_tokens (float | None, optional): Override | ||
| cost in USD per 1M input tokens. Both cost params must be set | ||
| to override the built-in defaults. | ||
| output_cost_per_million_tokens (float | None, optional): Override | ||
| cost in USD per 1M output tokens. | ||
|
|
||
| Example: | ||
| ```python | ||
|
|
@@ -60,6 +66,8 @@ def __init__( | |
| auth_token: str | None = None, | ||
| model_id: str | None = None, | ||
| client: Anthropic | None = None, | ||
| input_cost_per_million_tokens: float | None = None, | ||
| output_cost_per_million_tokens: float | None = None, | ||
| ) -> None: | ||
| self._model_id_value = ( | ||
| model_id or os.environ.get("VLM_PROVIDER_MODEL_ID") or _DEFAULT_MODEL_ID | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mlikasam-askui and @philipph-askui , we need to discuaa what is the proper way of Reading and Validating Env Variables. |
||
|
|
@@ -72,12 +80,22 @@ def __init__( | |
| base_url=base_url, | ||
| auth_token=auth_token, | ||
| ) | ||
| self._pricing = ModelPricing.for_model( | ||
| self._model_id_value, | ||
| input_cost_per_million_tokens=input_cost_per_million_tokens, | ||
| output_cost_per_million_tokens=output_cost_per_million_tokens, | ||
| ) | ||
|
|
||
| @property | ||
| @override | ||
| def model_id(self) -> str: | ||
| return self._model_id_value | ||
|
|
||
| @property | ||
| @override | ||
| def pricing(self) -> ModelPricing | None: | ||
| return self._pricing | ||
|
|
||
| @cached_property | ||
| def _messages_api(self) -> AnthropicMessagesApi: | ||
| """Lazily initialise the AnthropicMessagesApi on first use.""" | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,26 +5,64 @@ | |||||||
| from typing import TYPE_CHECKING | ||||||||
|
|
||||||||
| from opentelemetry import trace | ||||||||
| from pydantic import BaseModel | ||||||||
| from typing_extensions import override | ||||||||
|
|
||||||||
| from askui.models.shared.agent_message_param import UsageParam | ||||||||
| from askui.models.shared.conversation_callback import ConversationCallback | ||||||||
| from askui.reporting import NULL_REPORTER, Reporter | ||||||||
| from askui.reporting import NULL_REPORTER | ||||||||
|
|
||||||||
| if TYPE_CHECKING: | ||||||||
| from askui.models.shared.conversation import Conversation | ||||||||
| from askui.reporting import Reporter | ||||||||
| from askui.speaker.speaker import SpeakerResult | ||||||||
| from askui.utils.model_pricing import ModelPricing | ||||||||
|
|
||||||||
|
|
||||||||
| class UsageSummary(BaseModel): | ||||||||
| """Accumulated token usage and optional cost breakdown for a conversation. | ||||||||
|
|
||||||||
| Args: | ||||||||
| input_tokens (int | None): Total input tokens sent to the API. | ||||||||
| output_tokens (int | None): Total output tokens generated. | ||||||||
| cache_creation_input_tokens (int | None): Tokens used for cache creation. | ||||||||
| cache_read_input_tokens (int | None): Tokens read from cache. | ||||||||
| input_cost (float | None): Computed input cost in `currency`. | ||||||||
| output_cost (float | None): Computed output cost in `currency`. | ||||||||
| total_cost (float | None): Sum of `input_cost` and `output_cost`. | ||||||||
| currency (str | None): ISO 4217 currency code (e.g. ``"USD"``). | ||||||||
| input_cost_per_million_tokens (float | None): Rate used to compute `input_cost`. | ||||||||
| output_cost_per_million_tokens (float|None): Rate used to compute `output_cost`. | ||||||||
| """ | ||||||||
|
|
||||||||
| input_tokens: int | None = None | ||||||||
| output_tokens: int | None = None | ||||||||
| cache_creation_input_tokens: int | None = None | ||||||||
| cache_read_input_tokens: int | None = None | ||||||||
| input_cost: float | None = None | ||||||||
| output_cost: float | None = None | ||||||||
| total_cost: float | None = None | ||||||||
| currency: str | None = None | ||||||||
| input_cost_per_million_tokens: float | None = None | ||||||||
| output_cost_per_million_tokens: float | None = None | ||||||||
|
programminx-askui marked this conversation as resolved.
|
||||||||
|
|
||||||||
|
|
||||||||
| class UsageTrackingCallback(ConversationCallback): | ||||||||
| """Tracks token usage per step and reports a summary at conversation end. | ||||||||
|
|
||||||||
| Args: | ||||||||
| reporter: Reporter to write the final usage summary to. | ||||||||
| pricing: Pricing information for cost calculation. If ``None``, | ||||||||
| no cost data is included in the usage summary. | ||||||||
| """ | ||||||||
|
|
||||||||
| def __init__(self, reporter: Reporter = NULL_REPORTER) -> None: | ||||||||
| def __init__( | ||||||||
| self, | ||||||||
| reporter: Reporter = NULL_REPORTER, | ||||||||
| pricing: ModelPricing | None = None, | ||||||||
| ) -> None: | ||||||||
| self._reporter = reporter | ||||||||
| self._pricing = pricing | ||||||||
| self._accumulated_usage = UsageParam() | ||||||||
|
|
||||||||
| @override | ||||||||
|
|
@@ -43,7 +81,40 @@ def on_step_end( | |||||||
|
|
||||||||
| @override | ||||||||
| def on_conversation_end(self, conversation: Conversation) -> None: | ||||||||
| self._reporter.add_usage_summary(self._accumulated_usage.model_dump()) | ||||||||
| input_cost: float | None = None | ||||||||
| output_cost: float | None = None | ||||||||
| total_cost: float | None = None | ||||||||
|
programminx-askui marked this conversation as resolved.
Outdated
|
||||||||
| currency: str | None = None | ||||||||
| input_cost_per_million_tokens: float | None = None | ||||||||
| output_cost_per_million_tokens: float | None = None | ||||||||
|
programminx-askui marked this conversation as resolved.
Outdated
|
||||||||
| if self._pricing is not None: | ||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use early return instaed?
Suggested change
|
||||||||
| input_tokens = self._accumulated_usage.input_tokens or 0 | ||||||||
| output_tokens = self._accumulated_usage.output_tokens or 0 | ||||||||
| input_cost = ( | ||||||||
| input_tokens * self._pricing.input_cost_per_million_tokens / 1e6 | ||||||||
| ) | ||||||||
| output_cost = ( | ||||||||
| output_tokens * self._pricing.output_cost_per_million_tokens / 1e6 | ||||||||
| ) | ||||||||
| total_cost = input_cost + output_cost | ||||||||
| currency = self._pricing.currency | ||||||||
| input_cost_per_million_tokens = self._pricing.input_cost_per_million_tokens | ||||||||
| output_cost_per_million_tokens = ( | ||||||||
| self._pricing.output_cost_per_million_tokens | ||||||||
| ) | ||||||||
| summary = UsageSummary( | ||||||||
| input_tokens=self._accumulated_usage.input_tokens, | ||||||||
| output_tokens=self._accumulated_usage.output_tokens, | ||||||||
| cache_creation_input_tokens=self._accumulated_usage.cache_creation_input_tokens, | ||||||||
| cache_read_input_tokens=self._accumulated_usage.cache_read_input_tokens, | ||||||||
| input_cost=input_cost, | ||||||||
| output_cost=output_cost, | ||||||||
| total_cost=total_cost, | ||||||||
| currency=currency, | ||||||||
| input_cost_per_million_tokens=input_cost_per_million_tokens, | ||||||||
| output_cost_per_million_tokens=output_cost_per_million_tokens, | ||||||||
|
philipph-askui marked this conversation as resolved.
Outdated
|
||||||||
| ) | ||||||||
| self._reporter.add_usage_summary(summary) | ||||||||
|
|
||||||||
| @property | ||||||||
| def accumulated_usage(self) -> UsageParam: | ||||||||
|
|
||||||||
Uh oh!
There was an error while loading. Please reload this page.