tokens; } public function costUsd(): ?float { return $this->costUsd; } public function durationMs(): ?int { return $this->durationMs; } public function withCost(float $costUsd): self { return new self($this->tokens, $costUsd, $this->durationMs); } public function withDuration(int $durationMs): self { return new self($this->tokens, $this->costUsd, $durationMs); } public function withTokens(TokenCount $tokens): self { return new self($tokens, $this->costUsd, $this->durationMs); } public function hasCost(): bool { return $this->costUsd !== null; } public function hasDuration(): bool { return $this->durationMs !== null; } public function format(): string { $parts = [$this->tokens->format()]; if ($this->costUsd !== null) { $parts[] = sprintf('$%.4f', $this->costUsd); } if ($this->durationMs !== null) { if ($this->durationMs < 1000) { $parts[] = "{$this->durationMs}ms"; } else { $parts[] = sprintf('%.2fs', $this->durationMs / 1000); } } return implode(' | ', $parts); } public function toArray(): array { return [ 'tokens_input' => $this->tokens->input(), 'tokens_output' => $this->tokens->output(), 'tokens_total' => $this->tokens->total(), 'cost_usd' => $this->costUsd, 'duration_ms' => $this->durationMs, ]; } }