Include scheduled operations usage in weekly reports
This commit is contained in:
+47
-1
@@ -117,6 +117,50 @@ def codex_usage(session_root, thread_id, start, end):
|
||||
'measurement_note': '会话日志计数含重复处理的上下文和缓存;不是唯一文字数、周额度百分比或API账单。推理token包含于输出计数,不重复相加。'}
|
||||
|
||||
|
||||
def scheduled_codex_usage(directory, start, end):
|
||||
"""Read bounded cycle summaries separately from the interactive thread."""
|
||||
if not directory or not Path(directory).is_dir():
|
||||
return {'available': False, 'reason': '定时运营汇总目录未配置或不可用'}
|
||||
totals = {k: 0 for k in ('input_tokens', 'cached_input_tokens', 'output_tokens', 'reasoning_output_tokens')}
|
||||
runs = failed = 0
|
||||
for path in Path(directory).glob('*.summary.json'):
|
||||
try:
|
||||
record = json.loads(path.read_text())
|
||||
stamp = record.get('started_at')
|
||||
when = dt.datetime.fromisoformat(stamp.replace('Z', '+00:00')) if stamp else dt.datetime.strptime(record['id'][:16], '%Y%m%dT%H%M%SZ').replace(tzinfo=dt.timezone.utc)
|
||||
if when.tzinfo is None or not start <= when < end:
|
||||
continue
|
||||
runs += 1
|
||||
failed += record.get('status') != 'complete'
|
||||
for usage in record.get('usage', []):
|
||||
for k in totals:
|
||||
totals[k] += max(0, int(usage.get(k, 0)))
|
||||
except (ValueError, OSError, KeyError, TypeError, AttributeError):
|
||||
continue
|
||||
return {'available': True, 'runs': runs, 'failed_runs': failed, **totals,
|
||||
'measurement_note': '来自独立定时会话的运行汇总,与交互会话分开统计;失败或中断可能没有完整用量。'}
|
||||
|
||||
|
||||
def harness_run_usage(path, start, end):
|
||||
if not Path(path).exists():
|
||||
return {'available': False, 'reason': '后续 Harness 调用账本尚未创建'}
|
||||
result = {'available': True, 'runs': 0, 'input_tokens': 0, 'output_tokens': 0, 'cash_settlement_usd': None}
|
||||
for line in Path(path).read_text().splitlines():
|
||||
try:
|
||||
row = json.loads(line)
|
||||
when = dt.datetime.fromisoformat(row['at_utc'].replace('Z', '+00:00'))
|
||||
if when.tzinfo is None or not start <= when < end:
|
||||
continue
|
||||
result['runs'] += 1
|
||||
for usage in row.get('usage') or []:
|
||||
result['input_tokens'] += max(0, int(usage.get('inputTokens', 0)))
|
||||
result['output_tokens'] += max(0, int(usage.get('outputTokens', 0)))
|
||||
except (ValueError, KeyError, TypeError, AttributeError):
|
||||
continue
|
||||
result['measurement_note'] = '只统计自动账本创建后的调用;实际结算未知,不与历史快照重复计费。'
|
||||
return result
|
||||
|
||||
|
||||
def build_report(data_dir, snapshot=False, now=None):
|
||||
now = now or dt.datetime.now(TZ)
|
||||
now = now.astimezone(TZ)
|
||||
@@ -184,8 +228,10 @@ def build_report(data_dir, snapshot=False, now=None):
|
||||
'project_video_views': 0 if not published else None,
|
||||
'project_game_sessions': None,
|
||||
'codex': usage,
|
||||
'scheduled_codex': scheduled_codex_usage(collector.get('ops_cycle_summaries_dir'), begin_stamp, end_stamp),
|
||||
'deepseek': api or {'status': '未采集'},
|
||||
'measurement_notes': ['只统计运营账本登记稿件,不计入账户其他历史内容。', '当前会话包含PawPlay、运营基础设施和虚拟人设研究,token仅能按该共享会话汇总,未精确归因到单个方向。', '游戏离线运行且未接入统计,不能推导活跃、留存或转化率。', '已发布稿件存在时,播放数据需接入稿件快照采集,当前不填造数值。']
|
||||
'harness_new_runs': harness_run_usage(data_dir / 'harness-runs.jsonl', begin_stamp, end_stamp),
|
||||
'measurement_notes': ['只统计运营账本登记稿件,不计入账户其他历史内容。', '当前会话包含PawPlay、运营基础设施和虚拟人设研究,token仅能按该共享会话汇总,未精确归因到单个方向。定时任务作为独立会话分别统计。', '游戏离线运行且未接入统计,不能推导活跃、留存或转化率。', '已发布稿件存在时,播放数据需接入稿件快照采集,当前不填造数值。']
|
||||
},
|
||||
'costs': {
|
||||
'cash_usd': round(sum(e['amount'] for e in costs), 2) if config.get('cash_tracking_complete') else None,
|
||||
|
||||
Reference in New Issue
Block a user