Skip to content

Commit

Permalink
First draft done
Browse files Browse the repository at this point in the history
  • Loading branch information
ddebowczyk committed Jun 7, 2024
1 parent b49c9df commit bcd087d
Show file tree
Hide file tree
Showing 44 changed files with 303 additions and 396 deletions.
6 changes: 4 additions & 2 deletions notes/releases/r_0_8.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
- Composite language programs with Module classes (inspired by DSPy)
- Consolidated message building logic to support formats required by different APIs
- Script and Sections for better control over complex chat message sequences
- Support for new Anthropic tool calls mode
- Support for Cohere API
- Composite language programs with Module classes (inspired by DSPy)
- `FunctionCall` helper class for extracting arguments for callable objects
- Consolidated message building logic to support formats required by different APIs
- Refactored and simplified API client classes
- Additions to docs and examples
69 changes: 14 additions & 55 deletions src/ApiClient/ApiClient.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<?php
namespace Cognesy\Instructor\ApiClient;

use Cognesy\Instructor\ApiClient\Context\ApiRequestContext;
use Cognesy\Instructor\ApiClient\Contracts\CanCallApi;
use Cognesy\Instructor\Enums\Mode;
use Cognesy\Instructor\Events\EventDispatcher;
use Cognesy\Instructor\Events\Traits\HandlesEventListeners;
use Cognesy\Instructor\Events\Traits\HandlesEvents;
use Saloon\Enums\Method;

abstract class ApiClient implements CanCallApi
{
Expand All @@ -31,67 +33,24 @@ public function __construct(
/// PUBLIC API //////////////////////////////////////////////////////////////////////////////////////////

public function request(
array $messages, array $tools = [], array $toolChoice = [], array $responseFormat = [], string $model = '', array $options = []
array $body,
string $endpoint = '',
Method $method = Method::POST,

ApiRequestContext $context = null,
array $options = [],
array $data = [],
): static {
if (!isset($options['max_tokens'])) {
$options['max_tokens'] = $this->defaultMaxTokens;
}
$mode = match(true) {
!empty($tools) => Mode::Tools,
!empty($responseFormat) => Mode::Json,
default => Mode::MdJson,
};
$this->apiRequest = $this->apiRequestFactory->makeRequest(
requestClass: $this->getModeRequestClass($mode),
messages: $messages,
tools: $tools,
toolChoice: $toolChoice,
responseFormat: $responseFormat,
model: $this->getModel($model),
options: $options
);
return $this;
}

public function chatCompletion(array $messages, string $model = '', array $options = []): static {
if (!isset($options['max_tokens'])) {
$options['max_tokens'] = $this->defaultMaxTokens;
if (!isset($body['max_tokens'])) {
$body['max_tokens'] = $this->defaultMaxTokens;
}
$this->apiRequest = $this->apiRequestFactory->makeChatCompletionRequest(
requestClass: $this->getModeRequestClass(Mode::MdJson),
messages: $messages,
model: $this->getModel($model),
options: $options
);
return $this;
}

public function jsonCompletion(array $messages, array $responseFormat, string $model = '', array $options = []): static {
if (!isset($options['max_tokens'])) {
$options['max_tokens'] = $this->defaultMaxTokens;
}
$this->apiRequest = $this->apiRequestFactory->makeJsonCompletionRequest(
requestClass: $this->getModeRequestClass(Mode::Json),
messages: $messages,
responseFormat: $responseFormat,
model: $this->getModel($model),
options: $options
);
return $this;
}

public function toolsCall(array $messages, array $tools, array $toolChoice, string $model = '', array $options = []): static {
if (!isset($options['max_tokens'])) {
$options['max_tokens'] = $this->defaultMaxTokens;
if (!isset($body['model'])) {
$body['model'] = $this->defaultModel();
}
$this->apiRequest = $this->apiRequestFactory->makeToolsCallRequest(
requestClass: $this->getModeRequestClass(Mode::Tools),
messages: $messages,
tools: $tools,
toolChoice: $toolChoice,
model: $this->getModel($model),
options: $options
);
$this->apiRequest = $this->apiRequestFactory->makeRequest($this->getModeRequestClass(), $body, $endpoint, $method, $options, $data);
return $this;
}

Expand Down
13 changes: 7 additions & 6 deletions src/ApiClient/Contracts/CanCallApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@
use Cognesy\Instructor\Enums\Mode;
use Generator;
use GuzzleHttp\Promise\PromiseInterface;
use Saloon\Enums\Method;

interface CanCallApi
{
public function request(array $messages, array $tools = [], array $toolChoice = [], array $responseFormat = [], string $model = '', array $options = []): static;
public function request(array $body, string $endpoint, Method $method): static;

public function chatCompletion(array $messages, string $model = '', array $options = []): static;

public function jsonCompletion(array $messages, array $responseFormat, string $model = '', array $options = []): static;

public function toolsCall(array $messages, array $tools, array $toolChoice, string $model = '', array $options = []): static;
// public function chatCompletion(array $messages, string $model = '', array $options = []): static;
//
// public function jsonCompletion(array $messages, array $responseFormat, string $model = '', array $options = []): static;
//
// public function toolsCall(array $messages, array $tools, array $toolChoice, string $model = '', array $options = []): static;

public function get() : ApiResponse;

Expand Down
95 changes: 8 additions & 87 deletions src/ApiClient/Factories/ApiRequestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Cognesy\Instructor\ApiClient\Context\ApiRequestContext;
use Cognesy\Instructor\ApiClient\Requests\ApiRequest;
use Saloon\Enums\Method;

class ApiRequestFactory
{
Expand All @@ -13,95 +14,15 @@ public function __construct(

/**
* @param class-string $requestClass
*/
*/
public function makeRequest(
string $requestClass,
array $messages,
array $tools,
array $toolChoice,
array $responseFormat,
string $model = '',
array $options = []
array $body,
string $endpoint = '',
Method $method = Method::POST,
array $options = [],
array $data = [],
): ApiRequest {
/** @var ApiRequest $apiRequest */
$apiRequest = new $requestClass(...[
'messages' => $messages,
'tools' => $tools,
'toolChoice' => $toolChoice,
'responseFormat' => $responseFormat,
'model' => $model,
'options' => $options,
]);
$apiRequest->withContext($this->context);
return $apiRequest;
}

public function makeChatCompletionRequest(
string $requestClass,
array $messages,
string $model = '',
array $options = []
): ApiRequest {
return $this->fromClass(
requestClass: $requestClass,
args: [
'messages' => $messages,
'tools' => [],
'toolChoice' => [],
'responseFormat' => [],
'model' => $model,
'options' => $options,
]
);
}

public function makeJsonCompletionRequest(
string $requestClass,
array $messages,
array $responseFormat,
string $model = '',
array $options = []
): ApiRequest {
return $this->fromClass(
requestClass: $requestClass,
args: [
'messages' => $messages,
'tools' => [],
'toolChoice' => [],
'responseFormat' => $responseFormat,
'model' => $model,
'options' => $options,
]
);
}

public function makeToolsCallRequest(
string $requestClass,
array $messages,
array $tools,
array $toolChoice,
string $model = '',
array $options = []
): ApiRequest {
return $this->fromClass(
requestClass: $requestClass,
args: [
'messages' => $messages,
'tools' => $tools,
'toolChoice' => $toolChoice,
'responseFormat' => [],
'model' => $model,
'options' => $options,
]
);
}

/// INTERNAL ////////////////////////////////////////////////////////////////////////////////////////////

protected function fromClass(string $requestClass, array $args) : ApiRequest {
/** @var ApiRequest $apiRequest */
$apiRequest = new $requestClass(...$args);
$apiRequest->withContext($this->context);
return $apiRequest;
return new $requestClass($body, $endpoint, $method, $this->context, $options, $data);
}
}
62 changes: 43 additions & 19 deletions src/ApiClient/Requests/ApiRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

namespace Cognesy\Instructor\ApiClient\Requests;

use Cognesy\Instructor\ApiClient\Context\ApiRequestContext;
use Cognesy\Instructor\ApiClient\Responses\ApiResponse;
use Cognesy\Instructor\ApiClient\Responses\PartialApiResponse;
use Override;
use Saloon\CachePlugin\Contracts\Cacheable;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
Expand All @@ -22,44 +22,68 @@ abstract class ApiRequest extends Request implements HasBody, Cacheable
use Traits\HandlesDebug;

protected Method $method = Method::POST;
protected array $options = [];
protected array $requestBody = [];
protected array $data = [];

// TO BE DEPRECATED?
public array $messages = [];
public array $tools = [];
public string|array $toolChoice = [];
public string|array $responseFormat = [];
public string $model = '';

public function __construct(
public array $messages = [],
public array $tools = [],
public string|array $toolChoice = [],
public string|array $responseFormat = [],
public string $model = '',
public array $options = [],
public string $endpoint = '',
array $body = [],
string $endpoint = '',
Method $method = Method::POST,
//
ApiRequestContext $context = null,
array $options = [], // to consolidate into $context?
array $data = [], // to consolidate into $context?
) {
$this->context = $context;
$this->debug = $this->options['debug'] ?? false;
unset($this->options['debug']);

$this->cachingEnabled = $this->options['cache'] ?? false;
unset($this->options['cache']);

if ($this->cachingEnabled) {
if ($this->isStreamed()) {
throw new \Exception('Cannot use cache with streamed requests');
}
}

$this->options = $options;
$this->endpoint = $endpoint;
$this->method = $method;
$this->requestBody = $body;
$this->data = $data;

// maybe replace them with $requestBody
$this->messages = $body['messages'] ?? [];
$this->tools = $body['tools'] ?? [];
$this->toolChoice = $body['tool_choice'] ?? [];
$this->responseFormat = $body['response_format'] ?? [];
$this->model = $body['model'] ?? '';

$this->body()->setJsonFlags(JSON_UNESCAPED_SLASHES);
}

public function isStreamed(): bool {
return $this->options['stream'] ?? false;
return $this->requestBody['stream'] ?? false;
}

protected function defaultBody(): array {
return array_filter(
array_merge([
'messages' => $this->messages(),
'model' => $this->model,
'tools' => $this->tools(),
'tool_choice' => $this->getToolChoice(),
'response_format' => $this->getResponseFormat(),
], $this->options)
array_merge(
$this->requestBody,
[
'messages' => $this->messages(),
'model' => $this->model,
'tools' => $this->tools(),
'tool_choice' => $this->getToolChoice(),
'response_format' => $this->getResponseFormat(),
]
)
);
}

Expand Down
1 change: 1 addition & 0 deletions src/ApiClient/Requests/Traits/HandlesEndpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

trait HandlesEndpoint
{
protected ?string $endpoint = null;
protected string $defaultEndpoint = '/chat/completions';

public function resolveEndpoint() : string {
Expand Down
4 changes: 0 additions & 4 deletions src/ApiClient/Traits/HandlesDefaultModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,4 @@ trait HandlesDefaultModel
public function defaultModel() : string {
return $this->defaultModel;
}

protected function getModel(string $model) : string {
return $model ?: $this->defaultModel();
}
}
5 changes: 2 additions & 3 deletions src/Clients/Anthropic/AnthropicApiRequest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?php

namespace Cognesy\Instructor\Clients\Anthropic;

use Cognesy\Instructor\ApiClient\Requests\ApiRequest;
Expand All @@ -19,13 +18,13 @@ class AnthropicApiRequest extends ApiRequest
protected function defaultBody(): array {
return array_filter(
array_merge(
$this->requestBody,
[
'messages' => $this->messages(),
'model' => $this->model,
'tools' => $this->tools(),
'tool_choice' => $this->getToolChoice(),
],
$this->options
]
)
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Clients/Anthropic/AnthropicClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function __construct(
}

#[Override]
public function getModeRequestClass(Mode $mode) : string {
public function getModeRequestClass(Mode $mode = null) : string {
return AnthropicApiRequest::class;
}
}
1 change: 0 additions & 1 deletion src/Clients/Anyscale/AnyscaleApiRequest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?php

namespace Cognesy\Instructor\Clients\Anyscale;

use Cognesy\Instructor\ApiClient\Requests\ApiRequest;
Expand Down
Loading

0 comments on commit bcd087d

Please sign in to comment.