Skip to content

Commit

Permalink
refactor: ResponseInterface as Chain return value (#117)
Browse files Browse the repository at this point in the history
* refactor: ResponseInterface as Chain return value

* tests: Add Response Tests (#120)

* Add tests

* -

* -

* -

* -

* -

* -

* rebase

---------

Co-authored-by: Oskar Stark <[email protected]>
  • Loading branch information
chr-hertel and OskarStark authored Oct 5, 2024
1 parent c857660 commit ae5e028
Show file tree
Hide file tree
Showing 34 changed files with 331 additions and 240 deletions.
2 changes: 1 addition & 1 deletion examples/chat-claude-anthropic.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@
);
$response = $chain->call($messages);

echo $response.PHP_EOL;
echo $response->getContent().PHP_EOL;
2 changes: 1 addition & 1 deletion examples/chat-gpt-azure.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@
);
$response = $chain->call($messages);

echo $response.PHP_EOL;
echo $response->getContent().PHP_EOL;
2 changes: 1 addition & 1 deletion examples/chat-gpt-openai.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@
'max_tokens' => 500, // specific options just for this call
]);

echo $response.PHP_EOL;
echo $response->getContent().PHP_EOL;
2 changes: 1 addition & 1 deletion examples/image-describer.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@
);
$response = $chain->call($messages);

echo $response.PHP_EOL;
echo $response->getContent().PHP_EOL;
2 changes: 1 addition & 1 deletion examples/reasoning-openai.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@

$response = (new Chain($llm))->call(new MessageBag(Message::ofUser($prompt)));

echo $response.PHP_EOL;
echo $response->getContent().PHP_EOL;
2 changes: 1 addition & 1 deletion examples/store-mongodb-similarity-search.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,4 @@
);
$response = $chain->call($messages);

echo $response.PHP_EOL;
echo $response->getContent().PHP_EOL;
2 changes: 1 addition & 1 deletion examples/store-pinecone-similarity-search.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,4 @@
);
$response = $chain->call($messages);

echo $response.PHP_EOL;
echo $response->getContent().PHP_EOL;
4 changes: 1 addition & 3 deletions examples/stream-claude-anthropic.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@
'stream' => true, // enable streaming of response text
]);

assert($response instanceof Generator);

foreach ($response as $word) {
foreach ($response->getContent() as $word) {
echo $word;
}
echo PHP_EOL;
4 changes: 1 addition & 3 deletions examples/stream-gpt-openai.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@
'stream' => true, // enable streaming of response text
]);

assert($response instanceof Generator);

foreach ($response as $word) {
foreach ($response->getContent() as $word) {
echo $word;
}
echo PHP_EOL;
2 changes: 1 addition & 1 deletion examples/structured-output-math.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@
);
$response = $chain->call($messages, ['output_structure' => MathReasoning::class]);

dump($response);
dump($response->getContent());
2 changes: 1 addition & 1 deletion examples/toolbox-clock.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@
$messages = new MessageBag(Message::ofUser('What date and time is it?'));
$response = $chain->call($messages);

echo $response.PHP_EOL;
echo $response->getContent().PHP_EOL;
2 changes: 1 addition & 1 deletion examples/toolbox-serpapi.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@
$messages = new MessageBag(Message::ofUser('Who is the current chancellor of Germany?'));
$response = $chain->call($messages);

echo $response.PHP_EOL;
echo $response->getContent().PHP_EOL;
2 changes: 1 addition & 1 deletion examples/toolbox-weather.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@
$messages = new MessageBag(Message::ofUser('How is the weather currently in Berlin?'));
$response = $chain->call($messages);

echo $response.PHP_EOL;
echo $response->getContent().PHP_EOL;
2 changes: 1 addition & 1 deletion examples/toolbox-wikipedia.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@
$messages = new MessageBag(Message::ofUser('Who is the current chancellor of Germany?'));
$response = $chain->call($messages);

echo $response.PHP_EOL;
echo $response->getContent().PHP_EOL;
4 changes: 1 addition & 3 deletions examples/toolbox-youtube.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@
'stream' => true, // enable streaming of response text
]);

assert($response instanceof Generator);

foreach ($response as $word) {
foreach ($response->getContent() as $word) {
echo $word;
}
echo PHP_EOL;
5 changes: 2 additions & 3 deletions src/Anthropic/Model/Claude.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
use PhpLlm\LlmChain\Anthropic\Platform;
use PhpLlm\LlmChain\LanguageModel;
use PhpLlm\LlmChain\Message\MessageBag;
use PhpLlm\LlmChain\Response\Choice;
use PhpLlm\LlmChain\Response\Response;
use PhpLlm\LlmChain\Response\ResponseInterface;
use PhpLlm\LlmChain\Response\StreamResponse;
use PhpLlm\LlmChain\Response\TextResponse;

final class Claude implements LanguageModel
{
Expand Down Expand Up @@ -45,7 +44,7 @@ public function call(MessageBag $messages, array $options = []): ResponseInterfa
return new StreamResponse($this->convertStream($response));
}

return new Response(new Choice($response['content'][0]['text']));
return new TextResponse($response['content'][0]['text']);
}

public function supportsToolCalling(): bool
Expand Down
5 changes: 3 additions & 2 deletions src/Chain.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PhpLlm\LlmChain\Chain\OutputProcessor;
use PhpLlm\LlmChain\Exception\MissingModelSupport;
use PhpLlm\LlmChain\Message\MessageBag;
use PhpLlm\LlmChain\Response\ResponseInterface;

final readonly class Chain
{
Expand Down Expand Up @@ -39,7 +40,7 @@ public function __construct(
/**
* @param array<string, mixed> $options
*/
public function call(MessageBag $messages, array $options = []): string|object
public function call(MessageBag $messages, array $options = []): ResponseInterface
{
$input = new Input($this->llm, $messages, $options);
array_map(fn (InputProcessor $processor) => $processor->processInput($input), $this->inputProcessor);
Expand All @@ -59,6 +60,6 @@ public function call(MessageBag $messages, array $options = []): string|object
}
}

return $response->getContent();
return $response;
}
}
23 changes: 20 additions & 3 deletions src/OpenAI/Model/Gpt.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
use PhpLlm\LlmChain\OpenAI\Model\Gpt\Version;
use PhpLlm\LlmChain\OpenAI\Platform;
use PhpLlm\LlmChain\Response\Choice;
use PhpLlm\LlmChain\Response\Response;
use PhpLlm\LlmChain\Response\ChoiceResponse;
use PhpLlm\LlmChain\Response\ResponseInterface;
use PhpLlm\LlmChain\Response\StreamResponse;
use PhpLlm\LlmChain\Response\TextResponse;
use PhpLlm\LlmChain\Response\ToolCall;
use PhpLlm\LlmChain\Response\ToolCallResponse;

final class Gpt implements LanguageModel
{
Expand Down Expand Up @@ -45,7 +47,22 @@ public function call(MessageBag $messages, array $options = []): ResponseInterfa
return new StreamResponse($this->convertStream($response));
}

return new Response(...array_map([$this, 'convertChoice'], $response['choices']));
if (!isset($response['choices'])) {
throw new RuntimeException('Response does not contain choices');
}

/** @var Choice[] $choices */
$choices = array_map([$this, 'convertChoice'], $response['choices']);

if (1 !== count($choices)) {
return new ChoiceResponse(...$choices);
}

if ($choices[0]->hasToolCall()) {
return new ToolCallResponse(...$choices[0]->getToolCalls());
}

return new TextResponse($choices[0]->getContent());
}

public function supportsToolCalling(): bool
Expand Down Expand Up @@ -104,7 +121,7 @@ private function convertChoice(array $choice): Choice
return new Choice($choice['message']['content']);
}

throw new RuntimeException('Unsupported finish reason');
throw new RuntimeException(sprintf('Unsupported finish reason "%s".', $choice['finish_reason']));
}

/**
Expand Down
32 changes: 32 additions & 0 deletions src/Response/ChoiceResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\Response;

use PhpLlm\LlmChain\Exception\InvalidArgumentException;

final readonly class ChoiceResponse implements ResponseInterface
{
/**
* @var Choice[]
*/
private array $choices;

public function __construct(Choice ...$choices)
{
if (0 === count($choices)) {
throw new InvalidArgumentException('Response must have at least one choice.');
}

$this->choices = $choices;
}

/**
* @return Choice[]
*/
public function getContent(): array
{
return $this->choices;
}
}
59 changes: 0 additions & 59 deletions src/Response/Response.php

This file was deleted.

16 changes: 2 additions & 14 deletions src/Response/ResponseInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,7 @@
interface ResponseInterface
{
/**
* @return Choice[]
* @return string|iterable<mixed>|object|null
*/
public function getChoices(): array;

/**
* @return string|iterable<string>|null
*/
public function getContent(): string|iterable|null;

/**
* @return ToolCall[]
*/
public function getToolCalls(): array;

public function hasToolCalls(): bool;
public function getContent(): string|iterable|object|null;
}
15 changes: 0 additions & 15 deletions src/Response/StreamResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,8 @@ public function __construct(
) {
}

public function getChoices(): array
{
throw new \LogicException('Stream response does not have choices');
}

public function getContent(): \Generator
{
yield from $this->generator;
}

public function getToolCalls(): array
{
return [];
}

public function hasToolCalls(): bool
{
return false;
}
}
24 changes: 24 additions & 0 deletions src/Response/StructuredResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\Response;

final readonly class StructuredResponse implements ResponseInterface
{
/**
* @param object|array<string, mixed> $structuredOutput
*/
public function __construct(
private object|array $structuredOutput,
) {
}

/**
* @return object|array<string, mixed>
*/
public function getContent(): object|array
{
return $this->structuredOutput;
}
}
18 changes: 18 additions & 0 deletions src/Response/TextResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\Response;

final readonly class TextResponse implements ResponseInterface
{
public function __construct(
private string $content,
) {
}

public function getContent(): string
{
return $this->content;
}
}
Loading

0 comments on commit ae5e028

Please sign in to comment.