diff --git a/src/Response.php b/src/Response.php index a64457e..7628bb5 100644 --- a/src/Response.php +++ b/src/Response.php @@ -19,6 +19,16 @@ class Response use Conditionable; use Macroable; + /** + * @var array + */ + protected array $meta = []; + + /** + * @var array + */ + protected array $structured_content = []; + protected function __construct( protected Content $content, protected Role $role = Role::USER, @@ -27,6 +37,36 @@ protected function __construct( // } + /** + * @param array|null $meta + * @return ($meta is null ? array : self) + */ + public function meta(?array $meta = null): array|self + { + if (is_null($meta)) { + return $this->meta; + } + + $this->meta = array_merge($this->meta, $meta); + + return $this; + } + + /** + * @param array|null $structuredContent + * @return ($structuredContent is null ? array : self) + */ + public function structuredContent(?array $structuredContent = null): array|self + { + if (is_null($structuredContent)) { + return $this->structured_content; + } + + $this->structured_content = array_merge($this->structured_content, $structuredContent); + + return $this; + } + /** * @param array $params */ diff --git a/src/Server/Methods/CallTool.php b/src/Server/Methods/CallTool.php index e65bad3..fc1f975 100644 --- a/src/Server/Methods/CallTool.php +++ b/src/Server/Methods/CallTool.php @@ -61,13 +61,20 @@ public function handle(JsonRpcRequest $request, ServerContext $context): Generat } /** - * @return callable(Collection): array{content: array>, isError: bool} + * @return callable(Collection):array{ + * _meta?: array, + * content?: array>, + * isError?: bool, + * structuredContent?: array, + * } */ protected function serializable(Tool $tool): callable { - return fn (Collection $responses): array => [ + return fn (Collection $responses): array => array_filter([ 'content' => $responses->map(fn (Response $response): array => $response->content()->toTool($tool))->all(), 'isError' => $responses->contains(fn (Response $response): bool => $response->isError()), - ]; + 'structuredContent' => $responses->flatMap(fn (Response $response): array => $response->structuredContent())->all(), + '_meta' => $responses->flatMap(fn (Response $response): array => $response->meta())->all(), + ], filled(...)); } } diff --git a/tests/Unit/ResponseTest.php b/tests/Unit/ResponseTest.php index d9484d7..ff6965e 100644 --- a/tests/Unit/ResponseTest.php +++ b/tests/Unit/ResponseTest.php @@ -122,3 +122,31 @@ $content = $response->content(); expect((string) $content)->toBe(json_encode($data, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT)); }); + +it('handles adding meta to a text response', function (): void { + $response = Response::text('Hello world') + ->meta(['key1' => 'value1', 'key2' => 2]); + + expect($response->content())->toBeInstanceOf(Text::class); + expect($response->isNotification())->toBeFalse(); + expect($response->isError())->toBeFalse(); + expect($response->role())->toBe(Role::USER); + expect($response->meta())->toEqual(['key1' => 'value1', 'key2' => 2]); +}); + +it('handles adding structured content to a text response', function (): void { + $response = Response::text('Hello world') + ->structuredContent([ + 'section1' => ['item1', 'item2'], + 'section2' => ['item3', 'item4'], + ]); + + expect($response->content())->toBeInstanceOf(Text::class); + expect($response->isNotification())->toBeFalse(); + expect($response->isError())->toBeFalse(); + expect($response->role())->toBe(Role::USER); + expect($response->structuredContent())->toEqual([ + 'section1' => ['item1', 'item2'], + 'section2' => ['item3', 'item4'], + ]); +});