{
"tool_response": {
"type": "update",
"filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Domain\/Entity\/ChatMessage.php",
"content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Domain\\Entity;\n\n\/\/ @responsibility: Rich domain model for chat messages with business logic\n\nuse Domain\\ValueObject\\MessageContent;\nuse Domain\\ValueObject\\MessageMetadata;\nuse Domain\\ValueObject\\MessageRole;\nuse Domain\\ValueObject\\MessageTiming;\nuse Domain\\ValueObject\\TokenCount;\n\nclass ChatMessage\n{\n private ?int $id;\n private int $sessionId;\n private MessageRole $role;\n private MessageContent $content;\n private ?string $model;\n private TokenCount $tokens;\n private MessageTiming $timing;\n private MessageMetadata $metadata;\n private ?int $authorProfileId;\n private ?int $systemPromptId;\n private ?int $llmRequestId;\n private \\DateTimeImmutable $createdAt;\n\n private function __construct(\n ?int $id,\n int $sessionId,\n MessageRole $role,\n MessageContent $content,\n ?string $model,\n TokenCount $tokens,\n MessageTiming $timing,\n MessageMetadata $metadata,\n ?int $authorProfileId,\n ?int $systemPromptId,\n ?int $llmRequestId,\n \\DateTimeImmutable $createdAt\n ) {\n $this->id = $id;\n $this->sessionId = $sessionId;\n $this->role = $role;\n $this->content = $content;\n $this->model = $model;\n $this->tokens = $tokens;\n $this->timing = $timing;\n $this->metadata = $metadata;\n $this->authorProfileId = $authorProfileId;\n $this->systemPromptId = $systemPromptId;\n $this->llmRequestId = $llmRequestId;\n $this->createdAt = $createdAt;\n }\n\n \/\/ Factory methods for domain logic\n public static function createUserMessage(int $sessionId, string $content, ?int $authorProfileId = null): self\n {\n return new self(\n null,\n $sessionId,\n MessageRole::USER,\n MessageContent::fromString($content),\n null,\n TokenCount::zero(),\n MessageTiming::none(),\n MessageMetadata::empty(),\n $authorProfileId,\n null,\n null,\n new \\DateTimeImmutable()\n );\n }\n\n public static function createAssistantMessage(\n int $sessionId,\n string $content,\n string $model,\n TokenCount $tokens,\n MessageTiming $timing\n ): self {\n return new self(\n null,\n $sessionId,\n MessageRole::ASSISTANT,\n MessageContent::fromStringOrEmpty($content),\n $model,\n $tokens,\n $timing,\n MessageMetadata::empty(),\n null,\n null,\n null,\n new \\DateTimeImmutable()\n );\n }\n\n public static function createSystemMessage(int $sessionId, string $content, ?int $systemPromptId = null): self\n {\n return new self(\n null,\n $sessionId,\n MessageRole::SYSTEM,\n MessageContent::fromStringOrEmpty($content),\n null,\n TokenCount::zero(),\n MessageTiming::none(),\n MessageMetadata::empty(),\n null,\n $systemPromptId,\n null,\n new \\DateTimeImmutable()\n );\n }\n\n \/\/ Reconstitution from persistence (used by Factory)\n public static function reconstituteFromPersistence(\n ?int $id,\n int $sessionId,\n MessageRole $role,\n MessageContent $content,\n ?string $model,\n TokenCount $tokens,\n MessageTiming $timing,\n MessageMetadata $metadata,\n ?int $authorProfileId,\n ?int $systemPromptId,\n ?int $llmRequestId,\n \\DateTimeImmutable $createdAt\n ): self {\n return new self(\n $id,\n $sessionId,\n $role,\n $content,\n $model,\n $tokens,\n $timing,\n $metadata,\n $authorProfileId,\n $systemPromptId,\n $llmRequestId,\n $createdAt\n );\n }\n\n \/\/ Essential getters\n public function id(): ?int\n {\n return $this->id;\n }\n\n public function sessionId(): int\n {\n return $this->sessionId;\n }\n\n public function role(): MessageRole\n {\n return $this->role;\n }\n\n public function content(): MessageContent\n {\n return $this->content;\n }\n\n public function model(): ?string\n {\n return $this->model;\n }\n\n public function tokens(): TokenCount\n {\n return $this->tokens;\n }\n\n public function timing(): MessageTiming\n {\n return $this->timing;\n }\n\n public function metadata(): MessageMetadata\n {\n return $this->metadata;\n }\n\n public function authorProfileId(): ?int\n {\n return $this->authorProfileId;\n }\n\n public function systemPromptId(): ?int\n {\n return $this->systemPromptId;\n }\n\n public function llmRequestId(): ?int\n {\n return $this->llmRequestId;\n }\n\n public function createdAt(): \\DateTimeImmutable\n {\n return $this->createdAt;\n }\n\n \/\/ Business logic methods\n public function isUser(): bool\n {\n return $this->role->isUser();\n }\n\n public function isAssistant(): bool\n {\n return $this->role->isAssistant();\n }\n\n public function isSystem(): bool\n {\n return $this->role->isSystem();\n }\n\n public function hasTokens(): bool\n {\n return $this->tokens->hasTokens();\n }\n\n public function estimatedCostUsd(): float\n {\n return $this->tokens->estimatedCostUsd();\n }\n\n public function durationMs(): ?float\n {\n return $this->timing->durationMs();\n }\n\n \/\/ Mutation methods (return new instance for immutability)\n public function withId(int $id): self\n {\n $clone = clone $this;\n $clone->id = $id;\n\n return $clone;\n }\n\n public function withMetadata(MessageMetadata $metadata): self\n {\n $clone = clone $this;\n $clone->metadata = $metadata;\n\n return $clone;\n }\n\n public function withLlmRequestId(int $llmRequestId): self\n {\n $clone = clone $this;\n $clone->llmRequestId = $llmRequestId;\n\n return $clone;\n }\n\n public function withTiming(MessageTiming $timing): self\n {\n $clone = clone $this;\n $clone->timing = $timing;\n\n return $clone;\n }\n\n \/**\n * Convert to array for persistence.\n *\n * @return array<string, mixed>\n *\/\n public function toArray(): array\n {\n return [\n 'id' => $this->id,\n 'session_id' => $this->sessionId,\n 'role' => $this->role->value,\n 'content' => $this->content->value(),\n 'model' => $this->model,\n 'tokens_input' => $this->tokens->input() > 0 ? $this->tokens->input() : null,\n 'tokens_output' => $this->tokens->output() > 0 ? $this->tokens->output() : null,\n 'sources' => $this->metadata->sources() !== null ? json_encode($this->metadata->sources()) : null,\n 'start_microtime' => $this->timing->startMicrotime(),\n 'end_microtime' => $this->timing->endMicrotime(),\n 'author_profile_id' => $this->authorProfileId,\n 'system_prompt_id' => $this->systemPromptId,\n 'collections' => $this->metadata->collections() !== null ? json_encode($this->metadata->collections()) : null,\n 'context_limit' => $this->metadata->contextLimit(),\n 'chunks_used' => $this->metadata->chunksUsed() !== null ? json_encode($this->metadata->chunksUsed()) : null,\n 'llm_request_id' => $this->llmRequestId,\n 'created_at' => $this->createdAt->format('Y-m-d H:i:s'),\n ];\n }\n}\n",
"structuredPatch": [
{
"oldStart": 4,
"oldLines": 302,
"newStart": 4,
"newLines": 274,
"lines": [
" ",
" namespace Domain\\Entity;",
" ",
"-\/\/ @responsibility: Chat-Message-Entitaet mit Token-Tracking",
"+\/\/ @responsibility: Rich domain model for chat messages with business logic",
" ",
"+use Domain\\ValueObject\\MessageContent;",
"+use Domain\\ValueObject\\MessageMetadata;",
" use Domain\\ValueObject\\MessageRole;",
"+use Domain\\ValueObject\\MessageTiming;",
" use Domain\\ValueObject\\TokenCount;",
" ",
" class ChatMessage",
" {",
"- private ?int $id = null;",
"+ private ?int $id;",
" private int $sessionId;",
" private MessageRole $role;",
"- private string $content;",
"- private ?string $model = null;",
"+ private MessageContent $content;",
"+ private ?string $model;",
" private TokenCount $tokens;",
"- \/** @var array<mixed>|null *\/",
"- private ?array $sources = null;",
"- private ?float $startMicrotime = null;",
"- private ?float $endMicrotime = null;",
"- private ?int $authorProfileId = null;",
"- private ?int $systemPromptId = null;",
"- \/** @var array<string>|null *\/",
"- private ?array $collections = null;",
"- private ?int $contextLimit = null;",
"- \/** @var array<mixed>|null *\/",
"- private ?array $chunksUsed = null;",
"- private ?int $llmRequestId = null;",
"+ private MessageTiming $timing;",
"+ private MessageMetadata $metadata;",
"+ private ?int $authorProfileId;",
"+ private ?int $systemPromptId;",
"+ private ?int $llmRequestId;",
" private \\DateTimeImmutable $createdAt;",
" ",
"- public function __construct(int $sessionId, MessageRole $role, string $content)",
"- {",
"+ private function __construct(",
"+ ?int $id,",
"+ int $sessionId,",
"+ MessageRole $role,",
"+ MessageContent $content,",
"+ ?string $model,",
"+ TokenCount $tokens,",
"+ MessageTiming $timing,",
"+ MessageMetadata $metadata,",
"+ ?int $authorProfileId,",
"+ ?int $systemPromptId,",
"+ ?int $llmRequestId,",
"+ \\DateTimeImmutable $createdAt",
"+ ) {",
"+ $this->id = $id;",
" $this->sessionId = $sessionId;",
" $this->role = $role;",
" $this->content = $content;",
"- $this->tokens = TokenCount::zero();",
"- $this->createdAt = new \\DateTimeImmutable();",
"+ $this->model = $model;",
"+ $this->tokens = $tokens;",
"+ $this->timing = $timing;",
"+ $this->metadata = $metadata;",
"+ $this->authorProfileId = $authorProfileId;",
"+ $this->systemPromptId = $systemPromptId;",
"+ $this->llmRequestId = $llmRequestId;",
"+ $this->createdAt = $createdAt;",
" }",
" ",
"- \/\/ Factory methods",
"- public static function userMessage(int $sessionId, string $content): self",
"+ \/\/ Factory methods for domain logic",
"+ public static function createUserMessage(int $sessionId, string $content, ?int $authorProfileId = null): self",
" {",
"- return new self($sessionId, MessageRole::USER, $content);",
"+ return new self(",
"+ null,",
"+ $sessionId,",
"+ MessageRole::USER,",
"+ MessageContent::fromString($content),",
"+ null,",
"+ TokenCount::zero(),",
"+ MessageTiming::none(),",
"+ MessageMetadata::empty(),",
"+ $authorProfileId,",
"+ null,",
"+ null,",
"+ new \\DateTimeImmutable()",
"+ );",
" }",
" ",
"- public static function assistantMessage(int $sessionId, string $content, string $model): self",
"- {",
"- $message = new self($sessionId, MessageRole::ASSISTANT, $content);",
"- $message->model = $model;",
"-",
"- return $message;",
"+ public static function createAssistantMessage(",
"+ int $sessionId,",
"+ string $content,",
"+ string $model,",
"+ TokenCount $tokens,",
"+ MessageTiming $timing",
"+ ): self {",
"+ return new self(",
"+ null,",
"+ $sessionId,",
"+ MessageRole::ASSISTANT,",
"+ MessageContent::fromStringOrEmpty($content),",
"+ $model,",
"+ $tokens,",
"+ $timing,",
"+ MessageMetadata::empty(),",
"+ null,",
"+ null,",
"+ null,",
"+ new \\DateTimeImmutable()",
"+ );",
" }",
" ",
"- public static function systemMessage(int $sessionId, string $content): self",
"+ public static function createSystemMessage(int $sessionId, string $content, ?int $systemPromptId = null): self",
" {",
"- return new self($sessionId, MessageRole::SYSTEM, $content);",
"+ return new self(",
"+ null,",
"+ $sessionId,",
"+ MessageRole::SYSTEM,",
"+ MessageContent::fromStringOrEmpty($content),",
"+ null,",
"+ TokenCount::zero(),",
"+ MessageTiming::none(),",
"+ MessageMetadata::empty(),",
"+ null,",
"+ $systemPromptId,",
"+ null,",
"+ new \\DateTimeImmutable()",
"+ );",
" }",
" ",
"- \/\/ Getters",
"- public function getId(): ?int",
"+ \/\/ Reconstitution from persistence (used by Factory)",
"+ public static function reconstituteFromPersistence(",
"+ ?int $id,",
"+ int $sessionId,",
"+ MessageRole $role,",
"+ MessageContent $content,",
"+ ?string $model,",
"+ TokenCount $tokens,",
"+ MessageTiming $timing,",
"+ MessageMetadata $metadata,",
"+ ?int $authorProfileId,",
"+ ?int $systemPromptId,",
"+ ?int $llmRequestId,",
"+ \\DateTimeImmutable $createdAt",
"+ ): self {",
"+ return new self(",
"+ $id,",
"+ $sessionId,",
"+ $role,",
"+ $content,",
"+ $model,",
"+ $tokens,",
"+ $timing,",
"+ $metadata,",
"+ $authorProfileId,",
"+ $systemPromptId,",
"+ $llmRequestId,",
"+ $createdAt",
"+ );",
"+ }",
"+",
"+ \/\/ Essential getters",
"+ public function id(): ?int",
" {",
" return $this->id;",
" }",
" ",
"- public function getSessionId(): int",
"+ public function sessionId(): int",
" {",
" return $this->sessionId;",
" }",
" ",
"- public function getRole(): MessageRole",
"+ public function role(): MessageRole",
" {",
" return $this->role;",
" }",
" ",
"- public function getContent(): string",
"+ public function content(): MessageContent",
" {",
" return $this->content;",
" }",
" ",
"- public function getModel(): ?string",
"+ public function model(): ?string",
" {",
" return $this->model;",
" }",
" ",
"- public function getTokensInput(): ?int",
"+ public function tokens(): TokenCount",
" {",
"- return $this->tokens->input() > 0 ? $this->tokens->input() : null;",
"- }",
"-",
"- public function getTokensOutput(): ?int",
"- {",
"- return $this->tokens->output() > 0 ? $this->tokens->output() : null;",
"- }",
"-",
"- public function getTotalTokens(): int",
"- {",
"- return $this->tokens->total();",
"- }",
"-",
"- public function getTokenCount(): TokenCount",
"- {",
" return $this->tokens;",
" }",
" ",
"- \/**",
"- * Get estimated cost in USD.",
"- *\/",
"- public function getEstimatedCostUsd(): float",
"+ public function timing(): MessageTiming",
" {",
"- return $this->tokens->estimatedCostUsd();",
"+ return $this->timing;",
" }",
" ",
"- \/**",
"- * @return array<mixed>|null",
"- *\/",
"- public function getSources(): ?array",
"+ public function metadata(): MessageMetadata",
" {",
"- return $this->sources;",
"+ return $this->metadata;",
" }",
" ",
"- public function getStartMicrotime(): ?float",
"+ public function authorProfileId(): ?int",
" {",
"- return $this->startMicrotime;",
"- }",
"-",
"- public function getEndMicrotime(): ?float",
"- {",
"- return $this->endMicrotime;",
"- }",
"-",
"- public function getDurationMs(): ?float",
"- {",
"- if ($this->startMicrotime === null || $this->endMicrotime === null) {",
"- return null;",
"- }",
"-",
"- return ($this->endMicrotime - $this->startMicrotime) * 1000;",
"- }",
"-",
"- public function getAuthorProfileId(): ?int",
"- {",
" return $this->authorProfileId;",
" }",
" ",
"- public function getSystemPromptId(): ?int",
"+ public function systemPromptId(): ?int",
" {",
" return $this->systemPromptId;",
" }",
" ",
"- \/**",
"- * @return array<string>|null",
"- *\/",
"- public function getCollections(): ?array",
"+ public function llmRequestId(): ?int",
" {",
"- return $this->collections;",
"- }",
"-",
"- public function getContextLimit(): ?int",
"- {",
"- return $this->contextLimit;",
"- }",
"-",
"- \/**",
"- * @return array<mixed>|null",
"- *\/",
"- public function getChunksUsed(): ?array",
"- {",
"- return $this->chunksUsed;",
"- }",
"-",
"- public function getLlmRequestId(): ?int",
"- {",
" return $this->llmRequestId;",
" }",
" ",
"- public function getCreatedAt(): \\DateTimeImmutable",
"+ public function createdAt(): \\DateTimeImmutable",
" {",
" return $this->createdAt;",
" }",
" ",
"- \/\/ Setters",
"- public function setId(int $id): self",
"+ \/\/ Business logic methods",
"+ public function isUser(): bool",
" {",
"- $this->id = $id;",
"-",
"- return $this;",
"+ return $this->role->isUser();",
" }",
" ",
"- public function setModel(?string $model): self",
"+ public function isAssistant(): bool",
" {",
"- $this->model = $model;",
"-",
"- return $this;",
"+ return $this->role->isAssistant();",
" }",
" ",
"- public function setTokens(?int $input, ?int $output): self",
"+ public function isSystem(): bool",
" {",
"- $this->tokens = TokenCount::fromNullable($input, $output);",
"-",
"- return $this;",
"+ return $this->role->isSystem();",
" }",
" ",
"- public function setTokenCount(TokenCount $tokens): self",
"+ public function hasTokens(): bool",
" {",
"- $this->tokens = $tokens;",
"-",
"- return $this;",
"+ return $this->tokens->hasTokens();",
" }",
" ",
"- \/**",
"- * @param array<mixed>|null $sources",
"- *\/",
"- public function setSources(?array $sources): self",
"+ public function estimatedCostUsd(): float",
" {",
"- $this->sources = $sources;",
"-",
"- return $this;",
"+ return $this->tokens->estimatedCostUsd();",
" }",
" ",
"- public function setTiming(?float $start, ?float $end): self",
"+ public function durationMs(): ?float",
" {",
"- $this->startMicrotime = $start;",
"- $this->endMicrotime = $end;",
"-",
"- return $this;",
"+ return $this->timing->durationMs();",
" }",
" ",
"- public function setAuthorProfileId(?int $authorProfileId): self",
"+ \/\/ Mutation methods (return new instance for immutability)",
"+ public function withId(int $id): self",
" {",
"- $this->authorProfileId = $authorProfileId;",
"+ $clone = clone $this;",
"+ $clone->id = $id;",
" ",
"- return $this;",
"+ return $clone;",
" }",
" ",
"- public function setSystemPromptId(?int $systemPromptId): self",
"+ public function withMetadata(MessageMetadata $metadata): self",
" {",
"- $this->systemPromptId = $systemPromptId;",
"+ $clone = clone $this;",
"+ $clone->metadata = $metadata;",
" ",
"- return $this;",
"+ return $clone;",
" }",
" ",
"- \/**",
"- * @param array<string>|null $collections",
"- *\/",
"- public function setCollections(?array $collections): self",
"+ public function withLlmRequestId(int $llmRequestId): self",
" {",
"- $this->collections = $collections;",
"+ $clone = clone $this;",
"+ $clone->llmRequestId = $llmRequestId;",
" ",
"- return $this;",
"+ return $clone;",
" }",
" ",
"- public function setContextLimit(?int $contextLimit): self",
"+ public function withTiming(MessageTiming $timing): self",
" {",
"- $this->contextLimit = $contextLimit;",
"+ $clone = clone $this;",
"+ $clone->timing = $timing;",
" ",
"- return $this;",
"+ return $clone;",
" }",
" ",
" \/**",
"- * @param array<mixed>|null $chunksUsed",
"- *\/",
"- public function setChunksUsed(?array $chunksUsed): self",
"- {",
"- $this->chunksUsed = $chunksUsed;",
"-",
"- return $this;",
"- }",
"-",
"- public function setLlmRequestId(?int $llmRequestId): self",
"- {",
"- $this->llmRequestId = $llmRequestId;",
"-",
"- return $this;",
"- }",
"-",
"- public function setCreatedAt(\\DateTimeImmutable $createdAt): self",
"- {",
"- $this->createdAt = $createdAt;",
"-",
"- return $this;",
"- }",
"-",
"- \/**",
"- * Check if message is from user.",
"- *\/",
"- public function isUser(): bool",
"- {",
"- return $this->role->isUser();",
"- }",
"-",
"- \/**",
"- * Check if message is from assistant.",
"- *\/",
"- public function isAssistant(): bool",
"- {",
"- return $this->role->isAssistant();",
"- }",
"-",
"- \/**",
" * Convert to array for persistence.",
" *",
" * @return array<string, mixed>"
]
},
{
"oldStart": 310,
"oldLines": 82,
"newStart": 282,
"newLines": 20,
"lines": [
" 'id' => $this->id,",
" 'session_id' => $this->sessionId,",
" 'role' => $this->role->value,",
"- 'content' => $this->content,",
"+ 'content' => $this->content->value(),",
" 'model' => $this->model,",
" 'tokens_input' => $this->tokens->input() > 0 ? $this->tokens->input() : null,",
" 'tokens_output' => $this->tokens->output() > 0 ? $this->tokens->output() : null,",
"- 'sources' => $this->sources !== null ? json_encode($this->sources) : null,",
"- 'start_microtime' => $this->startMicrotime,",
"- 'end_microtime' => $this->endMicrotime,",
"+ 'sources' => $this->metadata->sources() !== null ? json_encode($this->metadata->sources()) : null,",
"+ 'start_microtime' => $this->timing->startMicrotime(),",
"+ 'end_microtime' => $this->timing->endMicrotime(),",
" 'author_profile_id' => $this->authorProfileId,",
" 'system_prompt_id' => $this->systemPromptId,",
"- 'collections' => $this->collections !== null ? json_encode($this->collections) : null,",
"- 'context_limit' => $this->contextLimit,",
"- 'chunks_used' => $this->chunksUsed !== null ? json_encode($this->chunksUsed) : null,",
"+ 'collections' => $this->metadata->collections() !== null ? json_encode($this->metadata->collections()) : null,",
"+ 'context_limit' => $this->metadata->contextLimit(),",
"+ 'chunks_used' => $this->metadata->chunksUsed() !== null ? json_encode($this->metadata->chunksUsed()) : null,",
" 'llm_request_id' => $this->llmRequestId,",
" 'created_at' => $this->createdAt->format('Y-m-d H:i:s'),",
" ];",
" }",
"-",
"- \/**",
"- * Create from database row.",
"- *",
"- * @param array<string, mixed> $data",
"- *\/",
"- public static function fromArray(array $data): self",
"- {",
"- $message = new self(",
"- (int) $data['session_id'],",
"- MessageRole::from($data['role']),",
"- $data['content']",
"- );",
"-",
"- if (isset($data['id'])) {",
"- $message->setId((int) $data['id']);",
"- }",
"- if (isset($data['model'])) {",
"- $message->setModel($data['model']);",
"- }",
"- if (isset($data['tokens_input']) || isset($data['tokens_output'])) {",
"- $message->setTokens(",
"- isset($data['tokens_input']) ? (int) $data['tokens_input'] : null,",
"- isset($data['tokens_output']) ? (int) $data['tokens_output'] : null",
"- );",
"- }",
"- if (isset($data['sources'])) {",
"- $sources = is_string($data['sources']) ? json_decode($data['sources'], true) : $data['sources'];",
"- $message->setSources(is_array($sources) ? $sources : null);",
"- }",
"- if (isset($data['start_microtime']) || isset($data['end_microtime'])) {",
"- $message->setTiming(",
"- isset($data['start_microtime']) ? (float) $data['start_microtime'] : null,",
"- isset($data['end_microtime']) ? (float) $data['end_microtime'] : null",
"- );",
"- }",
"- if (isset($data['author_profile_id'])) {",
"- $message->setAuthorProfileId((int) $data['author_profile_id']);",
"- }",
"- if (isset($data['system_prompt_id'])) {",
"- $message->setSystemPromptId((int) $data['system_prompt_id']);",
"- }",
"- if (isset($data['collections'])) {",
"- $collections = is_string($data['collections']) ? json_decode($data['collections'], true) : $data['collections'];",
"- $message->setCollections(is_array($collections) ? $collections : null);",
"- }",
"- if (isset($data['context_limit'])) {",
"- $message->setContextLimit((int) $data['context_limit']);",
"- }",
"- if (isset($data['chunks_used'])) {",
"- $chunks = is_string($data['chunks_used']) ? json_decode($data['chunks_used'], true) : $data['chunks_used'];",
"- $message->setChunksUsed(is_array($chunks) ? $chunks : null);",
"- }",
"- if (isset($data['llm_request_id'])) {",
"- $message->setLlmRequestId((int) $data['llm_request_id']);",
"- }",
"- if (isset($data['created_at'])) {",
"- $message->setCreatedAt(new \\DateTimeImmutable($data['created_at']));",
"- }",
"-",
"- return $message;",
"- }",
" }"
]
}
],
"originalFile": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Domain\\Entity;\n\n\/\/ @responsibility: Chat-Message-Entitaet mit Token-Tracking\n\nuse Domain\\ValueObject\\MessageRole;\nuse Domain\\ValueObject\\TokenCount;\n\nclass ChatMessage\n{\n private ?int $id = null;\n private int $sessionId;\n private MessageRole $role;\n private string $content;\n private ?string $model = null;\n private TokenCount $tokens;\n \/** @var array<mixed>|null *\/\n private ?array $sources = null;\n private ?float $startMicrotime = null;\n private ?float $endMicrotime = null;\n private ?int $authorProfileId = null;\n private ?int $systemPromptId = null;\n \/** @var array<string>|null *\/\n private ?array $collections = null;\n private ?int $contextLimit = null;\n \/** @var array<mixed>|null *\/\n private ?array $chunksUsed = null;\n private ?int $llmRequestId = null;\n private \\DateTimeImmutable $createdAt;\n\n public function __construct(int $sessionId, MessageRole $role, string $content)\n {\n $this->sessionId = $sessionId;\n $this->role = $role;\n $this->content = $content;\n $this->tokens = TokenCount::zero();\n $this->createdAt = new \\DateTimeImmutable();\n }\n\n \/\/ Factory methods\n public static function userMessage(int $sessionId, string $content): self\n {\n return new self($sessionId, MessageRole::USER, $content);\n }\n\n public static function assistantMessage(int $sessionId, string $content, string $model): self\n {\n $message = new self($sessionId, MessageRole::ASSISTANT, $content);\n $message->model = $model;\n\n return $message;\n }\n\n public static function systemMessage(int $sessionId, string $content): self\n {\n return new self($sessionId, MessageRole::SYSTEM, $content);\n }\n\n \/\/ Getters\n public function getId(): ?int\n {\n return $this->id;\n }\n\n public function getSessionId(): int\n {\n return $this->sessionId;\n }\n\n public function getRole(): MessageRole\n {\n return $this->role;\n }\n\n public function getContent(): string\n {\n return $this->content;\n }\n\n public function getModel(): ?string\n {\n return $this->model;\n }\n\n public function getTokensInput(): ?int\n {\n return $this->tokens->input() > 0 ? $this->tokens->input() : null;\n }\n\n public function getTokensOutput(): ?int\n {\n return $this->tokens->output() > 0 ? $this->tokens->output() : null;\n }\n\n public function getTotalTokens(): int\n {\n return $this->tokens->total();\n }\n\n public function getTokenCount(): TokenCount\n {\n return $this->tokens;\n }\n\n \/**\n * Get estimated cost in USD.\n *\/\n public function getEstimatedCostUsd(): float\n {\n return $this->tokens->estimatedCostUsd();\n }\n\n \/**\n * @return array<mixed>|null\n *\/\n public function getSources(): ?array\n {\n return $this->sources;\n }\n\n public function getStartMicrotime(): ?float\n {\n return $this->startMicrotime;\n }\n\n public function getEndMicrotime(): ?float\n {\n return $this->endMicrotime;\n }\n\n public function getDurationMs(): ?float\n {\n if ($this->startMicrotime === null || $this->endMicrotime === null) {\n return null;\n }\n\n return ($this->endMicrotime - $this->startMicrotime) * 1000;\n }\n\n public function getAuthorProfileId(): ?int\n {\n return $this->authorProfileId;\n }\n\n public function getSystemPromptId(): ?int\n {\n return $this->systemPromptId;\n }\n\n \/**\n * @return array<string>|null\n *\/\n public function getCollections(): ?array\n {\n return $this->collections;\n }\n\n public function getContextLimit(): ?int\n {\n return $this->contextLimit;\n }\n\n \/**\n * @return array<mixed>|null\n *\/\n public function getChunksUsed(): ?array\n {\n return $this->chunksUsed;\n }\n\n public function getLlmRequestId(): ?int\n {\n return $this->llmRequestId;\n }\n\n public function getCreatedAt(): \\DateTimeImmutable\n {\n return $this->createdAt;\n }\n\n \/\/ Setters\n public function setId(int $id): self\n {\n $this->id = $id;\n\n return $this;\n }\n\n public function setModel(?string $model): self\n {\n $this->model = $model;\n\n return $this;\n }\n\n public function setTokens(?int $input, ?int $output): self\n {\n $this->tokens = TokenCount::fromNullable($input, $output);\n\n return $this;\n }\n\n public function setTokenCount(TokenCount $tokens): self\n {\n $this->tokens = $tokens;\n\n return $this;\n }\n\n \/**\n * @param array<mixed>|null $sources\n *\/\n public function setSources(?array $sources): self\n {\n $this->sources = $sources;\n\n return $this;\n }\n\n public function setTiming(?float $start, ?float $end): self\n {\n $this->startMicrotime = $start;\n $this->endMicrotime = $end;\n\n return $this;\n }\n\n public function setAuthorProfileId(?int $authorProfileId): self\n {\n $this->authorProfileId = $authorProfileId;\n\n return $this;\n }\n\n public function setSystemPromptId(?int $systemPromptId): self\n {\n $this->systemPromptId = $systemPromptId;\n\n return $this;\n }\n\n \/**\n * @param array<string>|null $collections\n *\/\n public function setCollections(?array $collections): self\n {\n $this->collections = $collections;\n\n return $this;\n }\n\n public function setContextLimit(?int $contextLimit): self\n {\n $this->contextLimit = $contextLimit;\n\n return $this;\n }\n\n \/**\n * @param array<mixed>|null $chunksUsed\n *\/\n public function setChunksUsed(?array $chunksUsed): self\n {\n $this->chunksUsed = $chunksUsed;\n\n return $this;\n }\n\n public function setLlmRequestId(?int $llmRequestId): self\n {\n $this->llmRequestId = $llmRequestId;\n\n return $this;\n }\n\n public function setCreatedAt(\\DateTimeImmutable $createdAt): self\n {\n $this->createdAt = $createdAt;\n\n return $this;\n }\n\n \/**\n * Check if message is from user.\n *\/\n public function isUser(): bool\n {\n return $this->role->isUser();\n }\n\n \/**\n * Check if message is from assistant.\n *\/\n public function isAssistant(): bool\n {\n return $this->role->isAssistant();\n }\n\n \/**\n * Convert to array for persistence.\n *\n * @return array<string, mixed>\n *\/\n public function toArray(): array\n {\n return [\n 'id' => $this->id,\n 'session_id' => $this->sessionId,\n 'role' => $this->role->value,\n 'content' => $this->content,\n 'model' => $this->model,\n 'tokens_input' => $this->tokens->input() > 0 ? $this->tokens->input() : null,\n 'tokens_output' => $this->tokens->output() > 0 ? $this->tokens->output() : null,\n 'sources' => $this->sources !== null ? json_encode($this->sources) : null,\n 'start_microtime' => $this->startMicrotime,\n 'end_microtime' => $this->endMicrotime,\n 'author_profile_id' => $this->authorProfileId,\n 'system_prompt_id' => $this->systemPromptId,\n 'collections' => $this->collections !== null ? json_encode($this->collections) : null,\n 'context_limit' => $this->contextLimit,\n 'chunks_used' => $this->chunksUsed !== null ? json_encode($this->chunksUsed) : null,\n 'llm_request_id' => $this->llmRequestId,\n 'created_at' => $this->createdAt->format('Y-m-d H:i:s'),\n ];\n }\n\n \/**\n * Create from database row.\n *\n * @param array<string, mixed> $data\n *\/\n public static function fromArray(array $data): self\n {\n $message = new self(\n (int) $data['session_id'],\n MessageRole::from($data['role']),\n $data['content']\n );\n\n if (isset($data['id'])) {\n $message->setId((int) $data['id']);\n }\n if (isset($data['model'])) {\n $message->setModel($data['model']);\n }\n if (isset($data['tokens_input']) || isset($data['tokens_output'])) {\n $message->setTokens(\n isset($data['tokens_input']) ? (int) $data['tokens_input'] : null,\n isset($data['tokens_output']) ? (int) $data['tokens_output'] : null\n );\n }\n if (isset($data['sources'])) {\n $sources = is_string($data['sources']) ? json_decode($data['sources'], true) : $data['sources'];\n $message->setSources(is_array($sources) ? $sources : null);\n }\n if (isset($data['start_microtime']) || isset($data['end_microtime'])) {\n $message->setTiming(\n isset($data['start_microtime']) ? (float) $data['start_microtime'] : null,\n isset($data['end_microtime']) ? (float) $data['end_microtime'] : null\n );\n }\n if (isset($data['author_profile_id'])) {\n $message->setAuthorProfileId((int) $data['author_profile_id']);\n }\n if (isset($data['system_prompt_id'])) {\n $message->setSystemPromptId((int) $data['system_prompt_id']);\n }\n if (isset($data['collections'])) {\n $collections = is_string($data['collections']) ? json_decode($data['collections'], true) : $data['collections'];\n $message->setCollections(is_array($collections) ? $collections : null);\n }\n if (isset($data['context_limit'])) {\n $message->setContextLimit((int) $data['context_limit']);\n }\n if (isset($data['chunks_used'])) {\n $chunks = is_string($data['chunks_used']) ? json_decode($data['chunks_used'], true) : $data['chunks_used'];\n $message->setChunksUsed(is_array($chunks) ? $chunks : null);\n }\n if (isset($data['llm_reques... [TRUNCATED-c8c691b08af3e145]"
}
}