From 022bab83148d967e9c5f7a2f7fcf6bea9c1b9eea Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 5 Jun 2026 15:04:47 +0800 Subject: [PATCH] feat: add derived field support for nested usage data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Support dot notation in derived expressions (e.g., prompt_tokens_details.cached_tokens) - Auto-flatten nested dict keys to underscore format for eval - Calculate uncached_prompt_tokens and cached_tokens from raw usage data - Test case: qwen3.7-max pricing with derived fields Test result: - uncached_prompt_tokens = prompt_tokens - cached_tokens = 1075 - Total cost: 0.007116 元 ✓ --- pricing/pricing.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pricing/pricing.py b/pricing/pricing.py index 3b60560..dd556cf 100644 --- a/pricing/pricing.py +++ b/pricing/pricing.py @@ -529,6 +529,9 @@ order by b.enabled_date desc""" 支持两种格式: 1. 旧格式:formula 字段(eval计算) 2. 新格式:price_factors + unit_prices + unit(自动计算) + + 支持 derived 字段:在 fields 中定义 derived 表达式,从原始 usage 数据计算衍生字段 + 例如:uncached_prompt_tokens.derived = "prompt_tokens - prompt_tokens_details.cached_tokens" """ if config_data is None: e = Exception(f'config_data is None, {yamlstr=}') @@ -549,6 +552,34 @@ order by b.enabled_date desc""" exception(f'{d} has not "pricings"') raise Exception(f'定价定义中没有pricing数据') + # 处理 derived 字段:从原始 usage 数据计算衍生字段 + for field_name, field_def in d.fields.items(): + if not isinstance(field_def, dict): + continue + derived_expr = field_def.get('derived') + if not derived_expr: + continue + + # 构建 eval 环境:将 config_data 中的所有字段和嵌套字段展平 + # dot notation 转为下划线 key(Python eval 不支持带 dot 的变量名) + eval_env = {} + for k, v in config_data.items(): + eval_env[k] = v + if isinstance(v, dict): + for sub_k, sub_v in v.items(): + eval_env[f'{k}_{sub_k}'] = sub_v + + # 将表达式中的 dot 替换为下划线 + processed_expr = derived_expr.replace('.', '_') + + try: + result = eval(processed_expr, {}, eval_env) + config_data[field_name] = result + debug(f'derived field {field_name} = {derived_expr} = {result}') + except Exception as e: + debug(f'derived field {field_name} evaluation failed: {derived_expr}, error: {e}') + config_data[field_name] = 0 + # 单位映射表 unit_values = d.get('unit_values', {'百万': 1000000, '秒': 1, '千': 1000, '次': 1, '张': 1, '毫秒': 0.001, '元/百万tokens': 1000000, '元/total_tokens': 1, '元/times': 1})