From fa26dd8b1654127701ead24f1e2feb306b9c4a14 Mon Sep 17 00:00:00 2001 From: Art Koval Date: Tue, 21 Apr 2026 17:59:56 +0300 Subject: [PATCH 1/2] report: add section05-costs, fix section02-tasks zeros - add section05-costs to report schema: total coins, USD cost, avg cost per conversation, budget utilization percentage - fix section02-tasks: populate from kanban data instead of hardcoded zeros - update instruction text: remove stale "section02 are zero" message trade-off: only counts completed task costs, not triage/scheduled overhead Co-Authored-By: Claude Opus 4.6 (1M context) --- flexus_simple_bots/karen/karen_bot.py | 37 ++++++++++++++++++++------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/flexus_simple_bots/karen/karen_bot.py b/flexus_simple_bots/karen/karen_bot.py index a7798410..8a6d544c 100644 --- a/flexus_simple_bots/karen/karen_bot.py +++ b/flexus_simple_bots/karen/karen_bot.py @@ -183,6 +183,17 @@ "sentiment_notes": {"type": "string", "order": 4, "title": "Sentiment Notes"}, }, }, + "section05-costs": { + "type": "object", + "title": "Token Costs", + "properties": { + "total_coins": {"type": "integer", "order": 0, "title": "Total Coins"}, + "total_cost_usd": {"type": "number", "order": 1, "title": "Total Cost (USD)"}, + "conversations_count": {"type": "integer", "order": 2, "title": "Conversations"}, + "avg_cost_per_conversation": {"type": "number", "order": 3, "title": "Avg Cost/Conversation (USD)"}, + "budget_utilization_pct": {"type": "number", "order": 4, "title": "Budget Utilization %"}, + }, + }, } REPORT_TOOL = ckit_cloudtool.CloudTool( @@ -344,6 +355,8 @@ async def handle_report( all_tasks = await ckit_kanban.bot_get_all_tasks(http, pid) done_tasks = [t for t in all_tasks if t.ktask_done_ts >= ts0] + total_coins = sum(t.ktask_coins for t in done_tasks) + total_budget = sum(t.ktask_budget for t in done_tasks) by_code = {} for t in done_tasks: c = (t.ktask_resolution_code or "UNKNOWN").upper() @@ -361,11 +374,11 @@ async def handle_report( "refund_amount": refund_amount, }, "section02-tasks": { - "tasks_completed": 0, - "tasks_success": 0, - "tasks_failed": 0, - "tasks_inconclusive": 0, - "tasks_irrelevant": 0, + "tasks_completed": len(done_tasks), + "tasks_success": by_code.get("SUCCESS", 0), + "tasks_failed": by_code.get("FAIL", 0), + "tasks_inconclusive": by_code.get("INCONCLUSIVE", 0), + "tasks_irrelevant": by_code.get("IRRELEVANT", 0), }, "section03-notes": { "notable_incidents": "", @@ -379,6 +392,13 @@ async def handle_report( "resolved_escalated": by_code.get("ESCALATED", 0), "sentiment_notes": "", }, + "section05-costs": { + "total_coins": total_coins, + "total_cost_usd": round(total_coins / 1_000_000, 2), + "conversations_count": len(done_tasks), + "avg_cost_per_conversation": round(total_coins / max(len(done_tasks), 1) / 1_000_000, 4), + "budget_utilization_pct": round(total_coins / max(total_budget, 1) * 100, 1), + }, } date_str = now.strftime("%Y%m%d") @@ -398,10 +418,9 @@ async def handle_report( return ( "✍️ %s\nmd5=%s\n\n%s\n\n" - "Task stats (section02-tasks) are zero — fill them using your kanban search tool. " - "Resolution outcomes (section04-resolution-summary) are pre-filled from resolution codes; add sentiment_notes if patterns stand out. " - "Then fill in notes. Use flexus_policy_document(op=\"update_at_location\", " - "args={\"p\": \"%s\", \"expected_md5\": \"%s\", \"updates\": [[\"karen-report.section02-tasks.tasks_completed\", ...], ...]})" + "Task stats and resolution outcomes are pre-filled from kanban. " + "Fill in the notes section, then save. Use flexus_policy_document(op=\"update_at_location\", " + "args={\"p\": \"%s\", \"expected_md5\": \"%s\", \"updates\": [[\"karen-report.section03-notes.notable_incidents\", ...], ...]})" ) % (path, result.md5_after, doc_text, path, result.md5_after) From 51db872f0526c1bba28fd9cf7099694935d867e5 Mon Sep 17 00:00:00 2001 From: Art Koval Date: Mon, 27 Apr 2026 19:18:29 +0300 Subject: [PATCH 2/2] =?UTF-8?q?report:=20address=20review=20=E2=80=94=20dr?= =?UTF-8?q?op=20USD=20column,=20defer=20task-stats=20fix=20to=20#340?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - drop total_cost_usd and avg_cost_per_conversation USD fields from section05-costs; coins are the source of truth (Humberto comment) - rename avg field to avg_coins_per_conversation, integer division - revert section02-tasks fix and instruction text update — that work belongs to #340 (Humberto comment) Co-Authored-By: Claude Opus 4.7 (1M context) --- flexus_simple_bots/karen/karen_bot.py | 30 ++++++++++++++------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/flexus_simple_bots/karen/karen_bot.py b/flexus_simple_bots/karen/karen_bot.py index 8a6d544c..59230ec6 100644 --- a/flexus_simple_bots/karen/karen_bot.py +++ b/flexus_simple_bots/karen/karen_bot.py @@ -188,10 +188,9 @@ "title": "Token Costs", "properties": { "total_coins": {"type": "integer", "order": 0, "title": "Total Coins"}, - "total_cost_usd": {"type": "number", "order": 1, "title": "Total Cost (USD)"}, - "conversations_count": {"type": "integer", "order": 2, "title": "Conversations"}, - "avg_cost_per_conversation": {"type": "number", "order": 3, "title": "Avg Cost/Conversation (USD)"}, - "budget_utilization_pct": {"type": "number", "order": 4, "title": "Budget Utilization %"}, + "conversations_count": {"type": "integer", "order": 1, "title": "Conversations"}, + "avg_coins_per_conversation": {"type": "integer", "order": 2, "title": "Avg Coins/Conversation"}, + "budget_utilization_pct": {"type": "number", "order": 3, "title": "Budget Utilization %"}, }, }, } @@ -353,6 +352,9 @@ async def handle_report( refunds = await ckit_erp.erp_table_data(http, "com_refund", ws_id, erp_schema.ComRefund, filters=f"refund_created_ts:>=:{ts0}", limit=1000) refund_amount = float(sum(r.refund_amount for r in refunds)) + # XXX bad idea: + # - there are infinite tasks here + # - model already asks kanban about all the counters all_tasks = await ckit_kanban.bot_get_all_tasks(http, pid) done_tasks = [t for t in all_tasks if t.ktask_done_ts >= ts0] total_coins = sum(t.ktask_coins for t in done_tasks) @@ -374,11 +376,11 @@ async def handle_report( "refund_amount": refund_amount, }, "section02-tasks": { - "tasks_completed": len(done_tasks), - "tasks_success": by_code.get("SUCCESS", 0), - "tasks_failed": by_code.get("FAIL", 0), - "tasks_inconclusive": by_code.get("INCONCLUSIVE", 0), - "tasks_irrelevant": by_code.get("IRRELEVANT", 0), + "tasks_completed": 0, + "tasks_success": 0, + "tasks_failed": 0, + "tasks_inconclusive": 0, + "tasks_irrelevant": 0, }, "section03-notes": { "notable_incidents": "", @@ -394,9 +396,8 @@ async def handle_report( }, "section05-costs": { "total_coins": total_coins, - "total_cost_usd": round(total_coins / 1_000_000, 2), "conversations_count": len(done_tasks), - "avg_cost_per_conversation": round(total_coins / max(len(done_tasks), 1) / 1_000_000, 4), + "avg_coins_per_conversation": total_coins // max(len(done_tasks), 1), "budget_utilization_pct": round(total_coins / max(total_budget, 1) * 100, 1), }, } @@ -418,9 +419,10 @@ async def handle_report( return ( "✍️ %s\nmd5=%s\n\n%s\n\n" - "Task stats and resolution outcomes are pre-filled from kanban. " - "Fill in the notes section, then save. Use flexus_policy_document(op=\"update_at_location\", " - "args={\"p\": \"%s\", \"expected_md5\": \"%s\", \"updates\": [[\"karen-report.section03-notes.notable_incidents\", ...], ...]})" + "Task stats (section02-tasks) are zero — fill them using your kanban search tool. " + "Resolution outcomes (section04-resolution-summary) are pre-filled from resolution codes; add sentiment_notes if patterns stand out. " + "Then fill in notes. Use flexus_policy_document(op=\"update_at_location\", " + "args={\"p\": \"%s\", \"expected_md5\": \"%s\", \"updates\": [[\"karen-report.section02-tasks.tasks_completed\", ...], ...]})" ) % (path, result.md5_after, doc_text, path, result.md5_after)