Conversation
Add `web_search_kcount` price field and `web_search_requests` usage field across the full stack (Python, JS, YAML data). Anthropic responses with `server_tool_use.web_search_requests` are now extracted automatically. OpenAI prices are included for manual count pass-through until extraction from Responses API output is added. Pricing: Anthropic $10/1k, OpenAI gpt-4o $25/1k, gpt-4.1/gpt-5+ $30/1k.
| if self.web_search_kcount is not None: | ||
| web_search_requests = getattr(usage, 'web_search_requests', None) | ||
| if web_search_requests: | ||
| total_price += self.web_search_kcount * web_search_requests / 1000 |
There was a problem hiding this comment.
🚩 AbstractUsage protocol intentionally omits web_search_requests
The AbstractUsage protocol at packages/python/genai_prices/types.py:191-226 does not include a web_search_requests property, even though the Usage dataclass and calc_price method now support it. The calc_price method works around this at line 667 with getattr(usage, 'web_search_requests', None). This is a deliberate backward-compatibility choice: existing AbstractUsage implementations (e.g. from third-party integrations) won't break. However, it means that any custom usage class implementing AbstractUsage but missing web_search_requests will silently ignore web search costs even for models with web_search_kcount set. This is likely fine for now (the PR description notes OpenAI extraction isn't wired up yet), but should be revisited when web search extraction is added for more providers.
Was this helpful? React with 👍 or 👎 to provide feedback.
- Add `file_search_requests` usage field and `file_search_kcount` pricing field across Python, JS, and YAML type definitions - Add `file_search_kcount: 2.5` to 11 OpenAI models supporting Responses API file search - Map `web_search` pricing in OpenRouter source via new `kcount()` helper - Add calc_price logic for file_search_kcount in Python and JS engines
There was a problem hiding this comment.
🚩 Python _extract_path raises unconditionally at line 483 for non-mapping non-None data — JS handles gracefully
The pre-existing _extract_path function at packages/python/genai_prices/types.py:483-484 raises ValueError unconditionally when the data after navigating through intermediate steps is not a Mapping and not None, regardless of whether required=False. The JS equivalent at packages/js/src/extractUsage.ts:104-109 correctly returns null for non-required paths in the same scenario. This PR introduces the first multi-step non-required path (['server_tool_use', 'web_search_requests']), making this code path newly reachable. In practice, the Anthropic API always returns server_tool_use as either None or a dict, so this divergence won't trigger. But if an unexpected type were received (e.g., an integer), Python would crash while JS would gracefully return null.
(Refers to lines 483-484)
Was this helpful? React with 👍 or 👎 to provide feedback.
prices/providers/openai.yml
Outdated
| input_mtok: 1.25 | ||
| cache_read_mtok: 0.125 | ||
| output_mtok: 10 | ||
| web_search_kcount: 30 |
…_use` system Replace hard-coded `web_search_kcount`/`file_search_kcount` fields and `web_search_requests`/`file_search_requests` usage fields with a generic dict-based system using `ToolUseUnit` Literal for type enforcement. Adding a new tool type now requires adding one string to `ToolUseUnit` instead of new fields across 6+ files.
Expand the set of supported non-token pricing units so the system is ready for image generation, TTS, video processing, and other count-based pricing as providers adopt them.
Providers can define arbitrary tool use unit names in YAML without being constrained by a type-level enum. Routing in extraction logic now checks if dest is a known UsageField (token field); anything else goes to the tool_use dict.
There was a problem hiding this comment.
🚩 Pre-existing date mismatch between JS dataset test and Python dataset generation
The JS dataset test at packages/js/src/__tests__/dataset.test.ts:89 uses new Date(2025, 11, 6, 12, 0, 0) which is December 6, 2025 in local time (JS months are 0-indexed). The Python dataset generation at tests/dataset/extract_usages.py:115 uses datetime.datetime(2025, 11, 6, 12, 0, 0, tzinfo=datetime.timezone.utc) which is November 6, 2025 in UTC.
This is a pre-existing discrepancy (not introduced by this PR). It doesn't currently cause test failures because all date-sensitive price constraints (like the o3 model's start_date of 2025-06-10) resolve to the same price tier for both dates. However, if a future price change has a start_date between November and December 2025, the JS and Python tests would compute different prices for the same dataset row.
Was this helpful? React with 👍 or 👎 to provide feedback.
| tool_use_kcount: | ||
| web_search: 30 | ||
| file_search: 2.5 |
There was a problem hiding this comment.
🚩 OpenAI web search pricing not yet auto-extracted — manual pass-through only
The PR description notes that OpenAI prices are included for 'manual count pass-through until extraction from Responses API output is added.' OpenAI models have tool_use_kcount prices defined (e.g., web_search: 30 for gpt-4.1 at prices/providers/openai.yml:298), but the OpenAI extractors (chat and responses flavors) don't have any mapping for web search extraction. This means OpenAI web search costs will only be calculated if the caller manually provides tool_use: {web_search: N} in the Usage object. The Anthropic extractor, by contrast, auto-extracts from server_tool_use.web_search_requests. This asymmetry is documented in the PR but worth noting for follow-up.
Was this helpful? React with 👍 or 👎 to provide feedback.

Summary
web_search_kcountprice field andweb_search_requestsusage field across Python, JS, and YAML dataserver_tool_use.web_search_requestsare now extracted automatically via the default extractorTest plan
make build-pricesrebuilds data filesmake package-dataregenerates package datamake format && make lint && make typecheckall passmake test- 350 passed, 67 xfailed, dataset stable