(entries []TokenUsageEntry)
| 216 | } |
| 217 | |
| 218 | func extractAmbientContextMetrics(entries []TokenUsageEntry) *AmbientContextMetrics { |
| 219 | if len(entries) == 0 { |
| 220 | return nil |
| 221 | } |
| 222 | |
| 223 | type orderedTokenEntry struct { |
| 224 | entry TokenUsageEntry |
| 225 | timestamp time.Time |
| 226 | hasTimestamp bool |
| 227 | order int |
| 228 | } |
| 229 | |
| 230 | ordered := make([]orderedTokenEntry, 0, len(entries)) |
| 231 | for i, entry := range entries { |
| 232 | ts, hasTimestamp := parseTokenUsageTimestamp(entry.Timestamp) |
| 233 | ordered = append(ordered, orderedTokenEntry{ |
| 234 | entry: entry, |
| 235 | timestamp: ts, |
| 236 | hasTimestamp: hasTimestamp, |
| 237 | order: i, |
| 238 | }) |
| 239 | } |
| 240 | |
| 241 | slices.SortStableFunc(ordered, func(left, right orderedTokenEntry) int { |
| 242 | if left.hasTimestamp && right.hasTimestamp { |
| 243 | switch { |
| 244 | case left.timestamp.Before(right.timestamp): |
| 245 | return -1 |
| 246 | case right.timestamp.Before(left.timestamp): |
| 247 | return 1 |
| 248 | default: |
| 249 | return 0 |
| 250 | } |
| 251 | } |
| 252 | if left.hasTimestamp != right.hasTimestamp { |
| 253 | if left.hasTimestamp { |
| 254 | return -1 |
| 255 | } |
| 256 | return 1 |
| 257 | } |
| 258 | if left.order < right.order { |
| 259 | return -1 |
| 260 | } |
| 261 | if left.order > right.order { |
| 262 | return 1 |
| 263 | } |
| 264 | return 0 |
| 265 | }) |
| 266 | |
| 267 | firstCall := ordered[0].entry |
| 268 | return &AmbientContextMetrics{ |
| 269 | InputTokens: firstCall.InputTokens, |
| 270 | CachedTokens: firstCall.CacheReadTokens, |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | func parseTokenUsageTimestamp(value string) (time.Time, bool) { |
| 275 | if value == "" { |
no test coverage detected