Skip to content

Commit

Permalink
Refactoring of request processing - in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
ddebowczyk committed Oct 5, 2024
1 parent d20cf91 commit c033c89
Show file tree
Hide file tree
Showing 21 changed files with 190 additions and 116 deletions.
4 changes: 1 addition & 3 deletions docs/cookbook/examples/advanced/custom_prompts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 1 addition & 4 deletions docs/cookbook/examples/advanced/demonstrations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 1 addition & 6 deletions docs/cookbook/examples/basics/self_correction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
5 changes: 1 addition & 4 deletions docs/cookbook/examples/troubleshooting/on_event.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
2 changes: 1 addition & 1 deletion examples/A01_Basics/SelfCorrection/run.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion examples/A02_Advanced/CustomPrompts/run.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions examples/A02_Advanced/ProvidingExamples/run.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions examples/A03_Troubleshooting/OnEvent/run.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions src/Core/PartialsGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public function resetPartialResponse() : void {
/**
* @param Generator<PartialLLMResponse> $stream
* @param ResponseModel $responseModel
* @return Generator<mixed>
* @return Generator<PartialLLMResponse>
*/
public function getPartialResponses(Generator $stream, ResponseModel $responseModel) : Generator {
// reset state
Expand Down Expand Up @@ -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()));

Expand Down
27 changes: 6 additions & 21 deletions src/Core/RequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}

/**
Expand All @@ -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(),
Expand All @@ -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<mixed>
* @return Generator<PartialLLMResponse|LLMResponse>
*/
protected function streamResponseFor(Request $request) : Generator {
$this->init();
Expand All @@ -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 {
Expand All @@ -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,
Expand All @@ -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());

Expand Down
4 changes: 2 additions & 2 deletions src/Core/ResponseGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
29 changes: 29 additions & 0 deletions src/Events/HttpClient/RequestSentToLLM.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php
namespace Cognesy\Instructor\Events\HttpClient;

use Cognesy\Instructor\Events\Event;
use Cognesy\Instructor\Utils\Json\Json;
use Psr\Log\LogLevel;

class RequestSentToLLM extends Event
{
public $logLevel = LogLevel::INFO;

public function __construct(
public string $url,
public string $method,
public array $headers,
public array $body,
) {
parent::__construct();
}

public function __toString(): string {
return Json::encode([
'url' => $this->url,
'method' => $this->method,
'headers' => $this->headers,
'body' => $this->body,
]);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?php
namespace Cognesy\Instructor\Events\Request;
namespace Cognesy\Instructor\Events\HttpClient;

use Cognesy\Instructor\Data\Request;
use Cognesy\Instructor\Events\Event;
use Cognesy\Instructor\Utils\Json\Json;
use Psr\Log\LogLevel;
Expand All @@ -11,7 +10,10 @@ class RequestToLLMFailed extends Event
public $logLevel = LogLevel::ERROR;

public function __construct(
public Request $request,
public string $url,
public string $method,
public array $headers,
public array $body,
public string $errors,
) {
parent::__construct();
Expand All @@ -20,8 +22,11 @@ public function __construct(
public function __toString(): string
{
return Json::encode([
'url' => $this->url,
'method' => $this->method,
'headers' => $this->headers,
'body' => $this->body,
'errors' => $this->errors,
'request' => $this->request->toArray(),
]);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<?php
namespace Cognesy\Instructor\Events\Request;
namespace Cognesy\Instructor\Events\HttpClient;

use Cognesy\Instructor\Events\Event;
use Cognesy\Instructor\Extras\LLM\Data\LLMResponse;
use Cognesy\Instructor\Utils\Json\Json;
use Psr\Log\LogLevel;

Expand All @@ -11,13 +10,15 @@ class ResponseReceivedFromLLM extends Event
public $logLevel = LogLevel::INFO;

public function __construct(
public LLMResponse $response,
public int $statusCode,
) {
parent::__construct();
}

public function __toString(): string
{
return Json::encode($this->response);
return Json::encode([
'statusCode' => $this->statusCode
]);
}
}
22 changes: 0 additions & 22 deletions src/Events/Request/RequestSentToLLM.php

This file was deleted.

31 changes: 23 additions & 8 deletions src/Extras/Http/Drivers/GuzzleHttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.");
}
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/Extras/Http/HttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -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}"),
};
}
Expand Down
Loading

0 comments on commit c033c89

Please sign in to comment.