parseMessageUsage converts a map representation of message usage to chat.Usage Note: The embedded chat.Usage fields use snake_case JSON tags (input_tokens, etc.) while Cost and Model don't have JSON tags and serialize with capitalized names.
(m map[string]any)
| 324 | // Note: The embedded chat.Usage fields use snake_case JSON tags (input_tokens, etc.) |
| 325 | // while Cost and Model don't have JSON tags and serialize with capitalized names. |
| 326 | func parseMessageUsage(m map[string]any) *chat.Usage { |
| 327 | usage := &chat.Usage{} |
| 328 | |
| 329 | // Try snake_case first (from JSON serialization), then capitalized (fallback) |
| 330 | if v, ok := m["input_tokens"].(float64); ok { |
| 331 | usage.InputTokens = int64(v) |
| 332 | } else if v, ok := m["InputTokens"].(float64); ok { |
| 333 | usage.InputTokens = int64(v) |
| 334 | } |
| 335 | if v, ok := m["output_tokens"].(float64); ok { |
| 336 | usage.OutputTokens = int64(v) |
| 337 | } else if v, ok := m["OutputTokens"].(float64); ok { |
| 338 | usage.OutputTokens = int64(v) |
| 339 | } |
| 340 | if v, ok := m["cached_input_tokens"].(float64); ok { |
| 341 | usage.CachedInputTokens = int64(v) |
| 342 | } else if v, ok := m["CachedInputTokens"].(float64); ok { |
| 343 | usage.CachedInputTokens = int64(v) |
| 344 | } |
| 345 | if v, ok := m["cached_write_tokens"].(float64); ok { |
| 346 | usage.CacheWriteTokens = int64(v) |
| 347 | } else if v, ok := m["CacheWriteTokens"].(float64); ok { |
| 348 | usage.CacheWriteTokens = int64(v) |
| 349 | } |
| 350 | if v, ok := m["reasoning_tokens"].(float64); ok { |
| 351 | usage.ReasoningTokens = int64(v) |
| 352 | } else if v, ok := m["ReasoningTokens"].(float64); ok { |
| 353 | usage.ReasoningTokens = int64(v) |
| 354 | } |
| 355 | |
| 356 | return usage |
| 357 | } |
| 358 | |
| 359 | // parseEventTimestamp extracts the timestamp from an event map. |
| 360 | // Returns the timestamp string, falling back to current time if not present or invalid. |