cost_and_log_measurement - manishiitg/agentworks GitHub Wiki
This doc explains how workflow costs and logs are measured now.
It is not the same as workflow_monitoring.md.
-
workflow_monitoring.mdis about the user-facing observability surfaces - this doc is about the storage and measurement architecture underneath those surfaces
There are two separate data planes:
- driven by
token_usageevents from LLM calls - persisted into the workflow
costs/ledger - aggregated by run folder, phase, step, and model
- written explicitly by workflow controllers as files under
runs/.../logs/ - store execution results, conversations, validation results, learning traces, orchestration traces, and markers
Costs are not reconstructed from log files. Logs are not reconstructed from cost files.
The source of truth for workflow cost measurement is the token_usage event stream.
Current flow:
- An agent emits a
token_usageevent. - The context-aware bridge intercepts that event.
- It extracts:
- provider
- model ID
- prompt/input tokens
- completion/output tokens
- cache read and cache write tokens
- reasoning tokens
- LLM call count
- It persists those values directly to the
costs/ledger.
This happens in context_aware_bridge.go.
The cost ledger tracks per-model usage with pricing fields:
- input tokens
- output tokens
- cache tokens
- cache read tokens
- cache write tokens
- reasoning tokens
- LLM call count
- input cost
- output cost
- reasoning cost
- cache read cost
- cache write cost
- total cost
- context window usage
The main types are defined in:
Important nuance:
- cache reads and cache writes are tracked separately
- pricing is normalized or recomputed from model pricing helpers when files are read or merged
- the ledger stores both per-model totals and per-step-per-model totals
The current storage layout is under costs/.
Used for phase agents such as planning and builder-style workflow-phase sessions.
Files:
costs/phase/token_usage.jsoncosts/phase/daily/YYYY-MM-DD.json
These are written by PersistPhaseTokenUsage(...) in base_orchestrator_tokens.go.
Builder workflow-phase sessions also write phase cost data directly in server.go.
Used for normal workflow execution runs.
Files:
costs/execution/<group-or-__ungrouped__>/YYYY-MM-DD.json
Each daily file stores a map of run folders:
run_folders[runFolder] = TokenUsageFile
So one daily bucket can contain multiple runs for the same group on the same UTC date.
Used for evaluation execution.
Files:
- (retired)
costs/evaluation/<group-or-__ungrouped__>/YYYY-MM-DD.json— legacy ledger, no new writes
Evaluation uses the same ledger shape as execution, but under the evaluation scope.
Run-level cost data is aggregated into two main maps:
by_modelby_step_and_model
by_model:
- total usage across the whole run for each model
by_step_and_model:
- nested usage keyed by step and model
- current key format is usually
phase:stepID - older fallback format may use
phase:stepIndex
This is what lets the UI render:
- stage totals
- step-wise totals
- model-wise totals
- per-run totals
Older layouts still exist in some workspaces:
runs/<runFolder>/token_usage.json- (retired)
evaluation/runs/<runFolder>/token_usage.json— legacy path, no new writes - workspace-root
token_usage.jsonfor phase costs
Current behavior:
- these are migrated into the
costs/ledger when read or written - migration is handled automatically by the cost storage helpers
This logic is in cost_storage.go and token_usage_store.go.
The workflow cost API does not read a single per-run JSON file anymore.
Current behavior:
- reads
costs/phase/token_usage.json - reads
costs/phase/daily/* - reads all execution daily ledgers under
costs/execution/* - (retired) used to read evaluation daily ledgers under
costs/evaluation/* - merges run-folder totals across daily files
- returns execution and evaluation cost data side by side for each run folder
This happens in:
step_token_usage is a derived summary event, not the primary source of truth.
Current behavior:
- the system reads the persisted cost ledger for the current run
- aggregates the requested step across all models
- emits a summary event for UI display
This means:
-
token_usageevents drive persistence -
step_token_usageevents summarize persisted step totals
See base_orchestrator_tokens.go.
Workflow logs are explicit artifact files written by workflow controllers.
They are not reconstructed from the database event stream.
The main log families are:
- execution result JSON
- execution conversation JSON
- execution prompts JSON
- validation JSON
- pre-validation JSON
- learning execution JSONL
- learning conversation JSON
- orchestration execution JSONL
- todo-task execution JSONL
For a normal run folder, logs live under:
runs/{runFolder}/logs/...
Common files:
logs/{step-folder}/execution/execution-attempt-{N}-iteration-{M}.jsonlogs/{step-folder}/execution/execution-attempt-{N}-iteration-{M}-conversation.jsonlogs/{step-folder}/execution/execution-attempt-{N}-iteration-{M}-prompts.jsonlogs/{step-folder}/validation.jsonlogs/{step-folder}/validation-{N}.jsonlogs/{step-folder}/pre_validation.jsonlogs/{step-folder}/learning-execution.jsonlogs/{step-folder}/learning-conversation.jsonlogs/{step-folder}/orchestration-execution.jsonlogs/{step-folder}/todo-task-execution.json
There are also builder/session logs under:
builder/conversation/YYYY-MM-DD/session-{sessionId}-conversation.json
Main writers:
- execution result and conversation logs: controller_execution.go
- learning logs: controller_learning.go
- pre-validation logs: pre_validation.go
- workflow log API scanner: workflow.go
When a step is re-executed, older log files can be archived instead of being discarded.
Current behavior:
- selected log files are moved into
archived/{timestamp}under the step log folder - this preserves older attempts for debugging while keeping the active log folder readable
This is handled in controller_progress.go.
The workflow log API scans the run folder and reconstructs a structured view for the UI.
Current behavior:
- scans
runs/{runFolder}/logs/ - scans
runs/{runFolder}/execution/ - maps step folders back to plan metadata from
planning/plan.json - parses known file families into typed response sections
- exposes conversation file paths for lazy loading
This is why the execution log popup can show:
- per-step execution attempts
- validation history
- learning traces
- orchestration and todo-task traces
The implementation is in workflow.go.
Use this mental model:
- costs come from
token_usageevents and live incosts/ - logs come from controller-written files and live in
runs/.../logs/ -
step_token_usageis a derived UI summary from persisted costs -
/api/workflow/costsmerges the cost ledger -
/api/workflow/logsscans and structures the run artifacts