From c033c895b55da23d252d7a319a3221cdc4b66f90 Mon Sep 17 00:00:00 2001 From: ddebowczyk Date: Sat, 5 Oct 2024 17:03:23 +0200 Subject: [PATCH] Refactoring of request processing - in progress --- .../examples/advanced/custom_prompts.mdx | 4 +- .../examples/advanced/demonstrations.mdx | 5 +- .../examples/basics/self_correction.mdx | 7 +-- .../examples/troubleshooting/on_event.mdx | 5 +- examples/A01_Basics/SelfCorrection/run.php | 2 +- examples/A02_Advanced/CustomPrompts/run.php | 2 +- .../A02_Advanced/ProvidingExamples/run.php | 4 +- examples/A03_Troubleshooting/OnEvent/run.php | 4 +- src/Core/PartialsGenerator.php | 6 ++- src/Core/RequestHandler.php | 27 +++------- src/Core/ResponseGenerator.php | 4 +- src/Events/HttpClient/RequestSentToLLM.php | 29 +++++++++++ .../RequestToLLMFailed.php | 13 +++-- .../ResponseReceivedFromLLM.php | 9 ++-- src/Events/Request/RequestSentToLLM.php | 22 --------- src/Extras/Http/Drivers/GuzzleHttpClient.php | 31 +++++++++--- src/Extras/Http/HttpClient.php | 2 +- src/Extras/LLM/Data/LLMResponse.php | 38 ++++++++++++-- src/Extras/LLM/Data/PartialLLMResponse.php | 42 +++++++++++++++- src/Extras/LLM/Inference.php | 1 - src/Stream.php | 49 ++++++++++--------- 21 files changed, 190 insertions(+), 116 deletions(-) create mode 100644 src/Events/HttpClient/RequestSentToLLM.php rename src/Events/{Request => HttpClient}/RequestToLLMFailed.php (55%) rename src/Events/{Request => HttpClient}/ResponseReceivedFromLLM.php (61%) delete mode 100644 src/Events/Request/RequestSentToLLM.php diff --git a/docs/cookbook/examples/advanced/custom_prompts.mdx b/docs/cookbook/examples/advanced/custom_prompts.mdx index 3d7f3742..899f4581 100644 --- a/docs/cookbook/examples/advanced/custom_prompts.mdx +++ b/docs/cookbook/examples/advanced/custom_prompts.mdx @@ -19,9 +19,7 @@ customize how LLM is instructed to process the input. $loader = require 'vendor/autoload.php'; $loader->add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Events\Request\RequestSentToLLM; -use Cognesy\Instructor\Instructor; +use Cognesy\Instructor\Enums\Mode;use Cognesy\Instructor\Events\HttpClient\RequestSentToLLM;use Cognesy\Instructor\Instructor; class User { public int $age; diff --git a/docs/cookbook/examples/advanced/demonstrations.mdx b/docs/cookbook/examples/advanced/demonstrations.mdx index 17fefc2e..05751b23 100644 --- a/docs/cookbook/examples/advanced/demonstrations.mdx +++ b/docs/cookbook/examples/advanced/demonstrations.mdx @@ -19,10 +19,7 @@ is expected to be a JSON object. $loader = require 'vendor/autoload.php'; $loader->add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); -use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Events\Request\RequestSentToLLM; -use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Data\Example; +use Cognesy\Instructor\Data\Example;use Cognesy\Instructor\Enums\Mode;use Cognesy\Instructor\Events\HttpClient\RequestSentToLLM;use Cognesy\Instructor\Instructor; class User { public int $age; diff --git a/docs/cookbook/examples/basics/self_correction.mdx b/docs/cookbook/examples/basics/self_correction.mdx index 4cd12524..d37fadc4 100644 --- a/docs/cookbook/examples/basics/self_correction.mdx +++ b/docs/cookbook/examples/basics/self_correction.mdx @@ -19,12 +19,7 @@ until results meet the requirements or maxRetries is reached. $loader = require 'vendor/autoload.php'; $loader->add('Cognesy\\Instructor\\', __DIR__.'../../src/'); -use Cognesy\Instructor\Events\Request\RequestSentToLLM; -use Cognesy\Instructor\Events\Response\ResponseValidated; -use Cognesy\Instructor\Events\Response\ResponseValidationAttempt; -use Cognesy\Instructor\Events\Response\ResponseValidationFailed; -use Cognesy\Instructor\Instructor; -use Symfony\Component\Validator\Constraints as Assert; +use Cognesy\Instructor\Events\HttpClient\RequestSentToLLM;use Cognesy\Instructor\Events\Response\ResponseValidated;use Cognesy\Instructor\Events\Response\ResponseValidationAttempt;use Cognesy\Instructor\Events\Response\ResponseValidationFailed;use Cognesy\Instructor\Instructor;use Symfony\Component\Validator\Constraints as Assert; class UserDetails { diff --git a/docs/cookbook/examples/troubleshooting/on_event.mdx b/docs/cookbook/examples/troubleshooting/on_event.mdx index 57b766fe..d31e929a 100644 --- a/docs/cookbook/examples/troubleshooting/on_event.mdx +++ b/docs/cookbook/examples/troubleshooting/on_event.mdx @@ -25,10 +25,7 @@ and their properties. $loader = require 'vendor/autoload.php'; $loader->add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); -use Cognesy\Instructor\Events\Event; -use Cognesy\Instructor\Events\Request\RequestSentToLLM; -use Cognesy\Instructor\Events\Request\ResponseReceivedFromLLM; -use Cognesy\Instructor\Instructor; +use Cognesy\Instructor\Events\Event;use Cognesy\Instructor\Events\HttpClient\RequestSentToLLM;use Cognesy\Instructor\Events\HttpClient\ResponseReceivedFromLLM;use Cognesy\Instructor\Instructor; class User { diff --git a/examples/A01_Basics/SelfCorrection/run.php b/examples/A01_Basics/SelfCorrection/run.php index 4cd12524..46105122 100644 --- a/examples/A01_Basics/SelfCorrection/run.php +++ b/examples/A01_Basics/SelfCorrection/run.php @@ -19,7 +19,7 @@ $loader = require 'vendor/autoload.php'; $loader->add('Cognesy\\Instructor\\', __DIR__.'../../src/'); -use Cognesy\Instructor\Events\Request\RequestSentToLLM; +use Cognesy\Instructor\Events\HttpClient\RequestSentToLLM; use Cognesy\Instructor\Events\Response\ResponseValidated; use Cognesy\Instructor\Events\Response\ResponseValidationAttempt; use Cognesy\Instructor\Events\Response\ResponseValidationFailed; diff --git a/examples/A02_Advanced/CustomPrompts/run.php b/examples/A02_Advanced/CustomPrompts/run.php index 3d7f3742..cc0ffec8 100644 --- a/examples/A02_Advanced/CustomPrompts/run.php +++ b/examples/A02_Advanced/CustomPrompts/run.php @@ -20,7 +20,7 @@ $loader->add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Events\Request\RequestSentToLLM; +use Cognesy\Instructor\Events\HttpClient\RequestSentToLLM; use Cognesy\Instructor\Instructor; class User { diff --git a/examples/A02_Advanced/ProvidingExamples/run.php b/examples/A02_Advanced/ProvidingExamples/run.php index 17fefc2e..a6edba95 100644 --- a/examples/A02_Advanced/ProvidingExamples/run.php +++ b/examples/A02_Advanced/ProvidingExamples/run.php @@ -19,10 +19,10 @@ $loader = require 'vendor/autoload.php'; $loader->add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); +use Cognesy\Instructor\Data\Example; use Cognesy\Instructor\Enums\Mode; -use Cognesy\Instructor\Events\Request\RequestSentToLLM; +use Cognesy\Instructor\Events\HttpClient\RequestSentToLLM; use Cognesy\Instructor\Instructor; -use Cognesy\Instructor\Data\Example; class User { public int $age; diff --git a/examples/A03_Troubleshooting/OnEvent/run.php b/examples/A03_Troubleshooting/OnEvent/run.php index 57b766fe..e5bae308 100644 --- a/examples/A03_Troubleshooting/OnEvent/run.php +++ b/examples/A03_Troubleshooting/OnEvent/run.php @@ -26,8 +26,8 @@ $loader->add('Cognesy\\Instructor\\', __DIR__ . '../../src/'); use Cognesy\Instructor\Events\Event; -use Cognesy\Instructor\Events\Request\RequestSentToLLM; -use Cognesy\Instructor\Events\Request\ResponseReceivedFromLLM; +use Cognesy\Instructor\Events\HttpClient\RequestSentToLLM; +use Cognesy\Instructor\Events\HttpClient\ResponseReceivedFromLLM; use Cognesy\Instructor\Instructor; class User diff --git a/src/Core/PartialsGenerator.php b/src/Core/PartialsGenerator.php index 6357760a..fe22f402 100644 --- a/src/Core/PartialsGenerator.php +++ b/src/Core/PartialsGenerator.php @@ -63,7 +63,7 @@ public function resetPartialResponse() : void { /** * @param Generator $stream * @param ResponseModel $responseModel - * @return Generator + * @return Generator */ public function getPartialResponses(Generator $stream, ResponseModel $responseModel) : Generator { // reset state @@ -108,7 +108,9 @@ public function getPartialResponses(Generator $stream, ResponseModel $responseMo } $this->events->dispatch(new PartialJsonReceived($this->responseJson)); - yield $result->unwrap(); + yield $partialResponse + ->withValue($result->unwrap()) + ->withContent($this->responseText); } $this->events->dispatch(new StreamedResponseFinished($this->lastPartialResponse())); diff --git a/src/Core/RequestHandler.php b/src/Core/RequestHandler.php index 0b353baf..4a022aa5 100644 --- a/src/Core/RequestHandler.php +++ b/src/Core/RequestHandler.php @@ -9,13 +9,11 @@ use Cognesy\Instructor\Events\Instructor\InstructorDone; use Cognesy\Instructor\Events\Instructor\ResponseGenerated; use Cognesy\Instructor\Events\Request\NewValidationRecoveryAttempt; -use Cognesy\Instructor\Events\Request\RequestSentToLLM; -use Cognesy\Instructor\Events\Request\RequestToLLMFailed; -use Cognesy\Instructor\Events\Request\ResponseReceivedFromLLM; use Cognesy\Instructor\Events\Request\ValidationRecoveryLimitReached; use Cognesy\Instructor\Extras\Http\Contracts\CanHandleHttp; use Cognesy\Instructor\Extras\LLM\Contracts\CanHandleInference; use Cognesy\Instructor\Extras\LLM\Data\LLMResponse; +use Cognesy\Instructor\Extras\LLM\Data\PartialLLMResponse; use Cognesy\Instructor\Extras\LLM\Inference; use Cognesy\Instructor\Extras\LLM\InferenceResponse; use Cognesy\Instructor\Stream; @@ -52,7 +50,7 @@ public function get() : mixed { } $result = $this->responseFor($this->request); $this->events->dispatch(new InstructorDone(['result' => $result])); - return $result; + return $result->value(); } /** @@ -72,13 +70,12 @@ public function stream() : Stream { /** * Generates response value */ - protected function responseFor(Request $request) : mixed { + protected function responseFor(Request $request) : LLMResponse { $this->init(); $processingResult = Result::failure("No response generated"); while ($processingResult->isFailure() && !$this->maxRetriesReached($request)) { $llmResponse = $this->getInference($request)->toLLMResponse(); - $llmResponse->content = match($request->mode()) { Mode::Text => $llmResponse->content, default => Json::from($llmResponse->content)->toString(), @@ -89,13 +86,13 @@ protected function responseFor(Request $request) : mixed { $value = $this->finalizeResult($processingResult, $request, $llmResponse, $partialResponses); - return $value; + return $llmResponse->withValue($value); } /** * Yields response value versions based on streamed responses * @param Request $request - * @return Generator + * @return Generator */ protected function streamResponseFor(Request $request) : Generator { $this->init(); @@ -112,7 +109,7 @@ protected function streamResponseFor(Request $request) : Generator { $value = $this->finalizeResult($processingResult, $request, $llmResponse, $partialResponses); - yield $value; + yield $llmResponse->withValue($value); } protected function init() : void { @@ -121,16 +118,6 @@ protected function init() : void { } protected function getInference(Request $request) : InferenceResponse { - $this->events->dispatch(new RequestSentToLLM($request)); - try { - return $this->makeInference($request); - } catch (Exception $e) { - $this->events->dispatch(new RequestToLLMFailed($request, $e->getMessage())); - throw $e; - } - } - - protected function makeInference(Request $request) : InferenceResponse { $inference = new Inference( connection: $this->connection, httpClient: $this->httpClient, @@ -150,8 +137,6 @@ protected function makeInference(Request $request) : InferenceResponse { } protected function processResponse(Request $request, LLMResponse $llmResponse, array $partialResponses) : Result { - $this->events->dispatch(new ResponseReceivedFromLLM($llmResponse)); - // we have LLMResponse here - let's process it: deserialize, validate, transform $processingResult = $this->responseGenerator->makeResponse($llmResponse, $request->responseModel()); diff --git a/src/Core/ResponseGenerator.php b/src/Core/ResponseGenerator.php index bd5fe582..5bbbab76 100644 --- a/src/Core/ResponseGenerator.php +++ b/src/Core/ResponseGenerator.php @@ -27,9 +27,9 @@ public function __construct( ) {} public function makeResponse(LLMResponse $response, ResponseModel $responseModel) : Result { - $result = Chain::from(fn() => $response->getJson()) + $result = Chain::from(fn() => $response->json()) ->through(fn($responseJson) => match(true) { - empty($responseJson) => Result::failure('No JSON found in the response'), + ($responseJson === '') => Result::failure('No JSON found in the response'), default => Result::success($responseJson) }) ->through(fn($responseJson) => $this->responseDeserializer->deserialize($responseJson, $responseModel)) diff --git a/src/Events/HttpClient/RequestSentToLLM.php b/src/Events/HttpClient/RequestSentToLLM.php new file mode 100644 index 00000000..2117872a --- /dev/null +++ b/src/Events/HttpClient/RequestSentToLLM.php @@ -0,0 +1,29 @@ + $this->url, + 'method' => $this->method, + 'headers' => $this->headers, + 'body' => $this->body, + ]); + } +} \ No newline at end of file diff --git a/src/Events/Request/RequestToLLMFailed.php b/src/Events/HttpClient/RequestToLLMFailed.php similarity index 55% rename from src/Events/Request/RequestToLLMFailed.php rename to src/Events/HttpClient/RequestToLLMFailed.php index 58f4b4b4..1db04405 100644 --- a/src/Events/Request/RequestToLLMFailed.php +++ b/src/Events/HttpClient/RequestToLLMFailed.php @@ -1,7 +1,6 @@ $this->url, + 'method' => $this->method, + 'headers' => $this->headers, + 'body' => $this->body, 'errors' => $this->errors, - 'request' => $this->request->toArray(), ]); } } \ No newline at end of file diff --git a/src/Events/Request/ResponseReceivedFromLLM.php b/src/Events/HttpClient/ResponseReceivedFromLLM.php similarity index 61% rename from src/Events/Request/ResponseReceivedFromLLM.php rename to src/Events/HttpClient/ResponseReceivedFromLLM.php index 01e9a26e..7d5ebc7b 100644 --- a/src/Events/Request/ResponseReceivedFromLLM.php +++ b/src/Events/HttpClient/ResponseReceivedFromLLM.php @@ -1,8 +1,7 @@ response); + return Json::encode([ + 'statusCode' => $this->statusCode + ]); } } \ No newline at end of file diff --git a/src/Events/Request/RequestSentToLLM.php b/src/Events/Request/RequestSentToLLM.php deleted file mode 100644 index 2260bd34..00000000 --- a/src/Events/Request/RequestSentToLLM.php +++ /dev/null @@ -1,22 +0,0 @@ -request->toArray()); - } -} \ No newline at end of file diff --git a/src/Extras/Http/Drivers/GuzzleHttpClient.php b/src/Extras/Http/Drivers/GuzzleHttpClient.php index 6903bb33..37158e64 100644 --- a/src/Extras/Http/Drivers/GuzzleHttpClient.php +++ b/src/Extras/Http/Drivers/GuzzleHttpClient.php @@ -2,9 +2,14 @@ namespace Cognesy\Instructor\Extras\Http\Drivers; +use Cognesy\Instructor\Events\EventDispatcher; +use Cognesy\Instructor\Events\HttpClient\RequestSentToLLM; +use Cognesy\Instructor\Events\HttpClient\RequestToLLMFailed; +use Cognesy\Instructor\Events\HttpClient\ResponseReceivedFromLLM; use Cognesy\Instructor\Extras\Debug\Debug; use Cognesy\Instructor\Extras\Http\Contracts\CanHandleHttp; use Cognesy\Instructor\Extras\Http\Data\HttpClientConfig; +use Exception; use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; use GuzzleHttp\Middleware; @@ -22,7 +27,9 @@ class GuzzleHttpClient implements CanHandleHttp public function __construct( protected HttpClientConfig $config, protected ?Client $httpClient = null, + protected ?EventDispatcher $events = null, ) { + $this->events = $events ?? new EventDispatcher(); if (isset($this->httpClient) && Debug::isEnabled()) { throw new InvalidArgumentException("Guzzle does not allow to inject debugging stack into existing client. Turn off debug or use default client."); } @@ -40,14 +47,22 @@ public function handle( string $method = 'POST', bool $streaming = false ) : ResponseInterface { - return $this->client->request($method, $url, [ - 'headers' => $headers, - 'json' => $body, - 'connect_timeout' => $this->config->connectTimeout ?? 3, - 'timeout' => $this->config->requestTimeout ?? 30, - 'debug' => Debug::isFlag('http.trace') ?? false, - 'stream' => $streaming, - ]); + $this->events->dispatch(new RequestSentToLLM($url, $method, $headers, $body)); + try { + $response = $this->client->request($method, $url, [ + 'headers' => $headers, + 'json' => $body, + 'connect_timeout' => $this->config->connectTimeout ?? 3, + 'timeout' => $this->config->requestTimeout ?? 30, + 'debug' => Debug::isFlag('http.trace') ?? false, + 'stream' => $streaming, + ]); + $this->events->dispatch(new ResponseReceivedFromLLM($response->getStatusCode())); + return $response; + } catch (Exception $e) { + $this->events->dispatch(new RequestToLLMFailed($url, $method, $headers, $body, $e->getMessage())); + throw $e; + } } protected function addDebugStack(HandlerStack $stack) : HandlerStack { diff --git a/src/Extras/Http/HttpClient.php b/src/Extras/Http/HttpClient.php index 8498a68d..55500709 100644 --- a/src/Extras/Http/HttpClient.php +++ b/src/Extras/Http/HttpClient.php @@ -48,7 +48,7 @@ public function get() : CanHandleHttp { private function makeDriver(HttpClientConfig $config) : CanHandleHttp { return match ($config->httpClientType) { - HttpClientType::Guzzle => new GuzzleHttpClient($config), + HttpClientType::Guzzle => new GuzzleHttpClient(config: $config, events: $this->events), default => throw new InvalidArgumentException("Client not supported: {$config->httpClientType->value}"), }; } diff --git a/src/Extras/LLM/Data/LLMResponse.php b/src/Extras/LLM/Data/LLMResponse.php index 7327739d..1b58d64b 100644 --- a/src/Extras/LLM/Data/LLMResponse.php +++ b/src/Extras/LLM/Data/LLMResponse.php @@ -6,6 +6,8 @@ class LLMResponse { + private mixed $value = null; + public function __construct( public string $content = '', public array $responseData = [], @@ -18,21 +20,49 @@ public function __construct( public int $cacheReadTokens = 0, ) {} - public function getJson(): string { - return Json::from($this->content)->toString(); - } + // STATIC //////////////////////////////////////////////// public static function fromPartialResponses(array $partialResponses) : LLMResponse { return (new self)->makeInstance($partialResponses); } + // PUBLIC //////////////////////////////////////////////// + + public function hasValue() : bool { + return $this->value !== null; + } + + public function withValue(mixed $value) : self { + $this->value = $value; + return $this; + } + + public function value() : mixed { + return $this->value; + } + + public function hasContent() : bool { + return $this->content !== ''; + } + + public function json(): string { + if (!$this->hasContent()) { + return ''; + } + return Json::from($this->content)->toString(); + } + public function hasToolCalls() : bool { return !empty($this->toolCalls); } // INTERNAL ////////////////////////////////////////////// - private function makeInstance(array $partialResponses) : self { + private function makeInstance(array $partialResponses = []) : self { + if (empty($partialResponses)) { + return $this; + } + $content = ''; foreach($partialResponses as $partialResponse) { if ($partialResponse === null) { diff --git a/src/Extras/LLM/Data/PartialLLMResponse.php b/src/Extras/LLM/Data/PartialLLMResponse.php index d9f58bfe..e2701d65 100644 --- a/src/Extras/LLM/Data/PartialLLMResponse.php +++ b/src/Extras/LLM/Data/PartialLLMResponse.php @@ -2,8 +2,13 @@ namespace Cognesy\Instructor\Extras\LLM\Data; +use Cognesy\Instructor\Utils\Json\Json; + class PartialLLMResponse { + private mixed $value = null; + private string $content = ''; + public function __construct( public string $delta = '', public array $responseData = [], @@ -15,4 +20,39 @@ public function __construct( public int $cacheCreationTokens = 0, public int $cacheReadTokens = 0, ) {} -} \ No newline at end of file + + // PUBLIC //////////////////////////////////////////////// + + public function hasValue() : bool { + return $this->value !== null; + } + + public function withValue(mixed $value) : self { + $this->value = $value; + return $this; + } + + public function value() : mixed { + return $this->value; + } + + public function hasContent() : bool { + return $this->content !== ''; + } + + public function withContent(string $content) : self { + $this->content = $content; + return $this; + } + + public function content() : string { + return $this->content; + } + + public function json(): string { + if (!$this->hasContent()) { + return ''; + } + return Json::fromPartial($this->content)->toString(); + } +} diff --git a/src/Extras/LLM/Inference.php b/src/Extras/LLM/Inference.php index 4404d35f..8458ec42 100644 --- a/src/Extras/LLM/Inference.php +++ b/src/Extras/LLM/Inference.php @@ -121,7 +121,6 @@ public function create( $request = new InferenceRequest( $messages, $model, $tools, $toolChoice, $responseFormat, $options, $mode, $this->cachedContext ?? null ); -dump($request); $this->events->dispatch(new InferenceRequested($request)); return new InferenceResponse( response: $this->driver->handle($request), diff --git a/src/Stream.php b/src/Stream.php index 8329a10c..91196f79 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -5,12 +5,14 @@ use Cognesy\Instructor\Events\EventDispatcher; use Cognesy\Instructor\Events\Instructor\InstructorDone; use Cognesy\Instructor\Events\Instructor\ResponseGenerated; +use Cognesy\Instructor\Extras\LLM\Data\LLMResponse; +use Cognesy\Instructor\Extras\LLM\Data\PartialLLMResponse; use Cognesy\Instructor\Extras\Sequence\Sequence; use Exception; class Stream { - private mixed $lastUpdate = null; + private PartialLLMResponse|LLMResponse|null $lastUpdate = null; public function __construct( private Iterable $stream, @@ -22,7 +24,7 @@ public function __construct( * current or final response from the stream */ public function getLastUpdate() : mixed { - return $this->lastUpdate; + return $this->lastUpdate->value(); } /** @@ -36,23 +38,23 @@ public function getIterator() : Iterable { * Returns a stream of partial updates */ public function partials() : Iterable { - foreach ($this->stream as $update) { - $this->lastUpdate = $update; - yield $update; + foreach ($this->stream as $partialResponse) { + $this->lastUpdate = $partialResponse; + $result = $partialResponse->value(); + yield $result; } - $this->events->dispatch(new ResponseGenerated($this->lastUpdate)); - $this->events->dispatch(new InstructorDone(['result' => $this->lastUpdate])); + $this->events->dispatch(new ResponseGenerated($result)); + $this->events->dispatch(new InstructorDone(['result' => $result])); } /** * Processes response stream and returns the final update */ public function final() : mixed { - $result = null; - foreach ($this->stream as $update) { - $this->lastUpdate = $update; - $result = $update; + foreach ($this->stream as $partialResponse) { + $this->lastUpdate = $partialResponse; } + $result = $this->lastUpdate->value(); $this->events->dispatch(new ResponseGenerated($result)); $this->events->dispatch(new InstructorDone(['result' => $result])); return $result; @@ -64,8 +66,9 @@ public function final() : mixed { public function sequence() : Iterable { $lastSequence = null; $lastSequenceCount = 1; - foreach ($this->stream as $update) { - $this->lastUpdate = $update; + foreach ($this->stream as $partialResponse) { + $this->lastUpdate = $partialResponse; + $update = $partialResponse->value(); if (!($update instanceof Sequence)) { throw new Exception('Expected a sequence update, got ' . get_class($update)); } @@ -90,9 +93,9 @@ public function sequence() : Iterable { * Processes response stream and calls a callback for each update */ private function each(callable $callback) : void { - foreach ($this->stream as $update) { - $this->lastUpdate = $update; - $callback($update); + foreach ($this->stream as $partialResponse) { + $this->lastUpdate = $partialResponse; + $callback($partialResponse->value()); } $this->events->dispatch(new ResponseGenerated($this->lastUpdate)); $this->events->dispatch(new InstructorDone(['result' => $this->lastUpdate])); @@ -103,9 +106,9 @@ private function each(callable $callback) : void { */ private function map(callable $callback) : array { $result = []; - foreach ($this->stream as $update) { - $this->lastUpdate = $update; - $result[] = $callback($update); + foreach ($this->stream as $partialResponse) { + $this->lastUpdate = $partialResponse; + $result[] = $callback($partialResponse->value()); } $this->events->dispatch(new ResponseGenerated($this->lastUpdate)); $this->events->dispatch(new InstructorDone(['result' => $result])); @@ -117,12 +120,12 @@ private function map(callable $callback) : array { */ private function flatMap(callable $callback, mixed $initial) : mixed { $result = $initial; - foreach ($this->stream as $update) { - $this->lastUpdate = $update; - $result = $callback($update, $result); + foreach ($this->stream as $partialResponse) { + $this->lastUpdate = $partialResponse; + $result = $callback($partialResponse->value(), $result); } $this->events->dispatch(new ResponseGenerated($result)); - $this->events->dispatch(new InstructorDone()); + $this->events->dispatch(new InstructorDone($result)); return $result; } }