<?php

namespace DeepSeek;

use DeepSeek\Contracts\ClientContract;
use DeepSeek\Contracts\Models\ResultContract;
use DeepSeek\Enums\Requests\ClientTypes;
use DeepSeek\Enums\Requests\EndpointSuffixes;
use DeepSeek\Resources\Resource;
use Psr\Http\Client\ClientInterface;
use DeepSeek\Factories\ApiFactory;
use DeepSeek\Enums\Queries\QueryRoles;
use DeepSeek\Enums\Requests\QueryFlags;
use DeepSeek\Enums\Configs\TemperatureValues;
use DeepSeek\Traits\Resources\{HasChat, HasCoder};
use DeepSeek\Traits\Client\HasToolsFunctionCalling;

class DeepSeekClient implements ClientContract
{
    use HasChat, HasCoder;
    use HasToolsFunctionCalling;
    protected ClientInterface $httpClient;
    protected array $queries = [];
    protected ?string $model;
    protected bool $stream;
    protected float $temperature;
    protected int $maxTokens;
    protected string $responseFormatType;
    protected ResultContract $result;
    protected string $requestMethod;
    protected ?string $endpointSuffixes;
    protected ?array $tools;
    public function __construct(ClientInterface $httpClient)
    {
        $this->httpClient = $httpClient;
        $this->model = null;
        $this->stream = false;
        $this->requestMethod = 'POST';
        $this->endpointSuffixes = EndpointSuffixes::CHAT->value;
        $this->temperature = (float) TemperatureValues::GENERAL_CONVERSATION->value;
        $this->maxTokens = (int) TemperatureValues::MAX_TOKENS->value;
        $this->responseFormatType = TemperatureValues::RESPONSE_FORMAT_TYPE->value;
        $this->tools = null;
    }
    public function run(): string
    {
        $requestData = [
            QueryFlags::MESSAGES->value => $this->queries,
            QueryFlags::MODEL->value    => $this->model,
            QueryFlags::STREAM->value   => $this->stream,
            QueryFlags::TEMPERATURE->value   => $this->temperature,
            QueryFlags::MAX_TOKENS->value   => $this->maxTokens,
            QueryFlags::TOOLS->value  => $this->tools,
            QueryFlags::RESPONSE_FORMAT->value  => [
                'type' => $this->responseFormatType
            ],
        ];
        $this->setResult((new Resource($this->httpClient, $this->endpointSuffixes))->sendRequest($requestData, $this->requestMethod));
        return $this->getResult()->getContent();
    }
    public static function build(string $apiKey, ?string $baseUrl = null, ?int $timeout = null, ?string $clientType = null): self
    {
        $clientType = $clientType ?? ClientTypes::GUZZLE->value;
        $httpClient = ApiFactory::build()
            ->setBaseUri($baseUrl)
            ->setTimeout($timeout)
            ->setKey($apiKey)
            ->run($clientType);
        return new self($httpClient);
    }
    public function query(string $content, ?string $role = "user"): self
    {
        $this->queries[] = $this->buildQuery($content, $role);
        return $this;
    }
    public function resetQueries()
    {
        $this->queries = [];
        return $this;
    }
    public function getModelsList(): self
    {
        $this->endpointSuffixes = EndpointSuffixes::MODELS_LIST->value;
        $this->requestMethod = 'GET';
        return $this;
    }
    public function withModel(?string $model = null): self
    {
        $this->model = $model;
        return $this;
    }
    public function withStream(bool $stream = true): self
    {
        $this->stream = $stream;
        return $this;
    }
    public function setTemperature(float $temperature): self
    {
        $this->temperature = $temperature;
        return $this;
    }
    public function setMaxTokens(int $maxTokens): self
    {
        $this->maxTokens = $maxTokens;
        return $this;
    }
    public function setResponseFormat(string $type): self
    {
        $this->responseFormatType = $type;
        return $this;
    }
    public function buildQuery(string $content, ?string $role = null): array
    {
        return [
            'role' => $role ?: QueryRoles::USER->value,
            'content' => $content
        ];
    }
    public function setResult(ResultContract $result)
    {
        $this->result = $result;
        return $this;
    }
    public function getResult(): ResultContract
    {
        return $this->result;
    }
}

<?php

namespace DeepSeek\Factories;

use DeepSeek\Contracts\Factories\ApiFactoryContract;
use DeepSeek\Enums\Configs\DefaultConfigs;
use DeepSeek\Enums\Requests\ClientTypes;
use DeepSeek\Enums\Requests\HeaderFlags;
use GuzzleHttp\Client;
use Psr\Http\Client\ClientInterface;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\Psr18Client;
use RuntimeException;
use InvalidArgumentException;

final class ApiFactory implements ApiFactoryContract
{
    protected string $apiKey;
    protected string $baseUrl;
    protected int $timeout;
    protected array $clientConfig;

    public static function build(): self
    {
        return new self();
    }

    public function setBaseUri(?string $baseUrl = null): self
    {
        $this->baseUrl = $baseUrl ? trim($baseUrl) : DefaultConfigs::BASE_URL->value;
        return $this;
    }

    public function setKey(string $apiKey): self
    {
        $this->apiKey = trim($apiKey);
        return $this;
    }

    public function setTimeout(?int $timeout = null): self
    {
        $this->timeout = $timeout ?: (int)DefaultConfigs::TIMEOUT->value;
        return $this;
    }

    public function initialize(): self
    {
        if (!isset($this->baseUrl)) {
            $this->setBaseUri();
        }

        if (!isset($this->apiKey)) {
            throw new RuntimeException('API key must be set using setKey() before initialization.');
        }

        if (!isset($this->timeout)) {
            $this->setTimeout();
        }

        $this->clientConfig = [
            HeaderFlags::BASE_URL->value => $this->baseUrl,
            HeaderFlags::TIMEOUT->value  => $this->timeout,
            HeaderFlags::HEADERS->value  => [
                HeaderFlags::AUTHORIZATION->value => 'Bearer ' . $this->apiKey,
                HeaderFlags::CONTENT_TYPE->value  => 'application/json',
            ],
        ];

        return $this;
    }

    public function run(?string $clientType = null): ClientInterface
    {
        $clientType = $clientType ?? ClientTypes::GUZZLE->value;

        if (!isset($this->clientConfig)) {
            $this->initialize();
        }

        return match (strtolower($clientType)) {
            ClientTypes::GUZZLE->value => new Client($this->clientConfig),
            ClientTypes::SYMFONY->value => new Psr18Client(HttpClient::create($this->clientConfig)),
            default => throw new InvalidArgumentException("Unsupported client type: {$clientType}")
        };
    }
}

<?php

declare(strict_types=1);

namespace DeepSeek\Resources;

use DeepSeek\Contracts\Models\ResultContract;
use DeepSeek\Contracts\Resources\ResourceContract;
use DeepSeek\Enums\Configs\DefaultConfigs;
use DeepSeek\Enums\Models;
use DeepSeek\Enums\Data\DataTypes;
use DeepSeek\Enums\Requests\EndpointSuffixes;
use DeepSeek\Enums\Requests\QueryFlags;
use DeepSeek\Models\BadResult;
use DeepSeek\Models\FailureResult;
use DeepSeek\Models\SuccessResult;
use DeepSeek\Traits\Queries\HasQueryParams;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Nyholm\Psr7\Factory\Psr17Factory;

class Resource implements ResourceContract
{
    use HasQueryParams;

    protected ClientInterface $client;
    protected ?string $endpointSuffixes;
    protected RequestFactoryInterface $requestFactory;
    protected StreamFactoryInterface $streamFactory;

    public function __construct(
        ClientInterface $client,
        ?string $endpointSuffixes = null,
        ?RequestFactoryInterface $requestFactory = null,
        ?StreamFactoryInterface $streamFactory = null
    ) {
        $this->client = $client;
        $this->endpointSuffixes = $endpointSuffixes ?: EndpointSuffixes::CHAT->value;
        $this->requestFactory = $requestFactory ?: new Psr17Factory();
        $this->streamFactory = $streamFactory ?: new Psr17Factory();
    }

    public function sendRequest(array $requestData, ?string $requestMethod = 'POST'): ResultContract
    {
        try {
            $request = $this->requestFactory->createRequest(
                $requestMethod,
                $this->getEndpointSuffix()
            );

            if ($requestMethod === 'POST') {
                $request = $request
                    ->withHeader('Content-Type', 'application/json')
                    ->withBody(
                        $this->streamFactory->createStream(
                            json_encode($this->resolveHeaders($requestData))
                        ));
            }

            $response = $this->client->sendRequest($request);

            return (new SuccessResult())->setResponse($response);
        } catch (BadResponseException $badResponse) {
            return (new BadResult())->setResponse($badResponse->getResponse());
        } catch (GuzzleException $error) {
            return new FailureResult($error->getCode(), $error->getMessage());
        } catch (\Exception $error) {
            return new FailureResult($error->getCode(), '{"error":"'.$error->getMessage().'"}');
        }
    }

    /**
     * Merge request data with default headers.
     *
     * This method merges the given query data with custom headers that are
     * prepared for the request.
     *
     * @param array $requestData The data to send in the request.
     * @return array The merged request data with default headers.
     */
    protected function resolveHeaders(array $requestData): array
    {
        return array_merge($requestData, $this->prepareCustomHeaderParams($requestData));
    }

    /**
     * Prepare the custom headers for the request.
     *
     * This method loops through the query parameters and applies the appropriate
     * type conversion before returning the final headers.
     *
     * @param array $query The data to send in the request.
     * @return array The custom headers for the request.
     */
    public function prepareCustomHeaderParams(array $query): array
    {
        $headers = [];
        $params = $this->getAllowedQueryParamsList();

        // Loop through the parameters and apply the conversion logic dynamically
        foreach ($params as $key => $type) {
            $headers[$key] = $this->getQueryParam($query, $key, $this->getDefaultForKey($key), $type);
        }

        return $headers;
    }

    /**
     * Get the endpoint suffix for the resource.
     *
     * This method returns the endpoint suffix that is used in the API URL.
     *
     * @return string The endpoint suffix.
     */
    public function getEndpointSuffix(): string
    {
        return $this->endpointSuffixes;
    }

    /**
     * Get the model associated with the resource.
     *
     * This method returns the default model value associated with the resource.
     *
     * @return string The default model value.
     */
    public function getDefaultModel(): string
    {
        return Models::CHAT->value;
    }

    /**
     * Check if stream is enabled or not.
     *
     * This method checks whether the streaming option is enabled based on the
     * default configuration.
     *
     * @return bool True if streaming is enabled, false otherwise.
     */
    public function getDefaultStream(): bool
    {
        return DefaultConfigs::STREAM->value === 'true';
    }

    /**
     * Get the list of query parameters and their corresponding types for conversion.
     *
     * This method returns an array of query keys and their associated data types
     * for use in preparing the custom headers.
     *
     * @return array An associative array of query keys and their data types.
     */
    protected function getAllowedQueryParamsList(): array
    {
        return [
            QueryFlags::MODEL->value => DataTypes::STRING->value,
            QueryFlags::STREAM->value => DataTypes::BOOL->value,
        ];
    }
}

<?php

namespace DeepSeek\Traits\Client;

use DeepSeek\Enums\Queries\QueryRoles;

trait HasToolsFunctionCalling
{
    /**
     * @param array $tools A list of tools the model may call.
     * @return self The current instance for method chaining.
     */
    public function setTools(array $tools): self
    {
        $this->tools = $tools;
        return $this;
    }

    /**
     * Add a query tool calls to the accumulated queries list.
     *
     * @param array $toolCalls The tool calls generated by the model, such as function calls.
     * @param string $content
     * @param string|null $role
     * @return self The current instance for method chaining.
     */
    public function queryToolCall(array $toolCalls, string $content, ?string $role = null): self
    {
        $this->queries[] = $this->buildToolCallQuery($toolCalls, $content, $role);
        return $this;
    }

    public function buildToolCallQuery(array $toolCalls, string $content, ?string $role = null): array
    {
        $query = [
            'role' => $role ?: QueryRoles::ASSISTANT->value,
            'tool_calls' => $toolCalls,
            'content' => $content,
        ];
        return $query;
    }

    /**
     * Add a query tool to the accumulated queries list.
     *
     * @param string $toolCallId
     * @param string $content
     * @param string|null $role
     * @return self The current instance for method chaining.
     */
    public function queryTool(string $toolCallId, string $content , ?string $role = null): self
    {
        $this->queries[] = $this->buildToolQuery($toolCallId, $content, $role);
        return $this;
    }

    public function buildToolQuery(string $toolCallId, string $content, ?string $role): array
    {
        $query = [
            'role' => $role ?: QueryRoles::TOOL->value,
            'tool_call_id' => $toolCallId,
            'content' => $content,
        ];
        return $query;
    }
}

<?php

declare(strict_types=1);

namespace DeepSeek\Models;

use DeepSeek\Contracts\Models\ResultContract;
use DeepSeek\Enums\Requests\HTTPState;
use Psr\Http\Message\ResponseInterface;

abstract class ResultAbstract implements ResultContract
{
    protected ?int $statusCode;
    protected ?string $content;
    /**
     * handel response coming from request
     * @var ResponseInterface|null
     */
    protected ?ResponseInterface $response;
    public function __construct(?int $statusCode = null, ?string $content = null)
    {
        $this->statusCode = $statusCode;
        $this->content = $content;
    }
    protected function setStatusCode(int $statusCode)
    {
        $this->statusCode = $statusCode;
    }
    public function getStatusCode(): int
    {
        return $this->statusCode;
    }
    protected function setContent(string $content): void
    {
        $this->content = $content;
    }
    public function getContent(): string
    {
        return $this->content;
    }
    public function setResponse(ResponseInterface $response): static
    {
        $this->response = $response;
        $this->setStatusCode($this->getResponse()->getStatusCode());
        $this->setContent($this->getResponse()->getBody()->getContents());
        return $this;
    }
    public function getResponse(): ResponseInterface
    {
        return $this->response;
    }
    public function isSuccess(): bool
    {
        return ($this->getStatusCode() === HTTPState::OK->value);
    }
}

<?php

namespace DeepSeek\Traits\Queries;

use DeepSeek\Enums\Data\DataTypes;
use DeepSeek\Enums\Requests\QueryFlags;

trait HasQueryParams
{
    /**
     * Helper method to get the query parameter or default value with type conversion.
     *
     * @param array $query
     * @param string $key
     * @param mixed $default
     * @param string $type
     * @return mixed
     */
    private function getQueryParam(array $query, string $key, $default, string $type): mixed
    {
        if (isset($query[$key])) {
            $value = $query[$key];
            return $this->convertValue($value, $type);
        }

        return $default;
    }

    /**
     * Convert the value to the specified type.
     *
     * @param mixed $value
     * @param string $type
     * @return mixed
     */
    private function convertValue($value, string $type): mixed
    {
        return match ($type) {
            DataTypes::STRING->value => (string)$value,
            DataTypes::INTEGER->value => (int)$value,
            DataTypes::FLOAT->value => (float)$value,
            DataTypes::ARRAY->value => (array)$value,
            DataTypes::OBJECT->value => (object)$value,
            DataTypes::BOOL->value => (bool)$value,
            DataTypes::JSON->value => json_decode((string)$value, true),
            default => $value,
        };
    }

    /**
     * Get default value for specific query keys.
     *
     * @param string $key
     * @return mixed
     */
    private function getDefaultForKey(string $key): mixed
    {
        return match ($key) {
            QueryFlags::MODEL->value => $this->getDefaultModel(),
            QueryFlags::STREAM->value => $this->getDefaultStream(),
            default => null,
        };
    }
}

<?php

namespace DeepSeek\Traits\Resources;

use DeepSeek\Resources\Chat;

trait HasChat
{
    /**
     * Send the accumulated queries to the Chat resource.
     *
     * @return string
     */
    public function chat(): string
    {
        $requestData = [
            'messages' => $this->queries,
            'model' => $this->model,
            'stream' => $this->stream,
        ];
        $this->queries = [];
        $this->setResult((new Chat($this->httpClient))->sendRequest($requestData));
        return $this->getResult()->getContent();
    }
}

<?php

namespace DeepSeek\Traits\Resources;

use DeepSeek\Resources\Coder;

trait HasCoder
{
    /**
     * Send the accumulated queries to the code resource.
     *
     * @return string
     */
    public function code(): string
    {
        $requestData = [
            'messages' => $this->queries,
            'model' => $this->model,
            'stream' => $this->stream,
        ];
        $this->queries = [];
        $this->setResult((new Coder($this->httpClient))->sendRequest($requestData));
        return $this->getResult()->getContent();
    }
}

<?php

namespace DeepSeek\Contracts;

interface ClientContract
{
    public function run(): string;
    public static function build(string $apiKey, ?string $baseUrl = null, ?int $timeout = null): self;
    public function query(string $content, ?string $role = "user"): self;
    public function getModelsList(): self;
    public function withModel(?string $model = null): self;
    public function withStream(bool $stream = true): self;
    public function buildQuery(string $content, ?string $role = null): array;
}

<?php

namespace DeepSeek\Contracts\Factories;

use DeepSeek\Factories\ApiFactory;
use GuzzleHttp\Client;
use Psr\Http\Client\ClientInterface;

interface ApiFactoryContract
{
    /**
     * Create a new instance of the factory.
     *
     * @return ApiFactory
     */
    public static function build(): ApiFactory;

    /**
     * Set the base URL for the API.
     *
     * @param string|null $baseUrl The base URL to set (optional).
     * @return ApiFactory
     */
    public function setBaseUri(?string $baseUrl = null): ApiFactory;

    /**
     * Set the API key for authentication.
     *
     * @param string $apiKey The API key to set.
     * @return ApiFactory
     */
    public function setKey(string $apiKey): ApiFactory;

    /**
     * Set the timeout for the API request.
     *
     * @param int|null $timeout The timeout value in seconds (optional).
     * @return ApiFactory
     */
    public function setTimeout(?int $timeout = null): ApiFactory;

    /**
     * Build and return http Client instance.
     *
     * @return ClientInterface
     */
    public function run(?string $clientType = null): ClientInterface;
}

<?php

declare(strict_types=1);

namespace DeepSeek\Resources;

use DeepSeek\Enums\Models;

class Coder extends Resource
{
    /**
     * Get the model associated with the resource.
     *
     * This method returns the default model value associated with the resource.
     *
     * @return string The default model value.
     */
    public function getDefaultModel(): string
    {
        return Models::CODER->value;
    }
}

<?php

namespace DeepSeek\Enums\Requests;

enum QueryFlags: string
{
    case MESSAGES = 'messages';
    case MODEL = 'model';
    case STREAM = 'stream';
    case TEMPERATURE = 'temperature';
    case MAX_TOKENS = 'max_tokens';
    case TOOLS = 'tools';
    case RESPONSE_FORMAT = 'response_format';
}

<?php

namespace DeepSeek\Contracts\Models;

interface ResultContract
{
    /**
     * result status code
     * @return int
     */
    public function getStatusCode(): int;

    /**
     * result content date as a string
     * @return string
     */
    public function getContent(): string;

    /**
     * if response status code is ok (200)
     * @return bool
     */
    public function isSuccess(): bool;
}

<?php

namespace DeepSeek\Contracts\Resources;

/**
 * Interface for defining the structure of resource classes.
 */
interface ResourceContract
{
    /**
     * Get the endpoint suffix for the resource.
     *
     * @return string
     */
    public function getEndpointSuffix(): string;

    /**
     * Get the model associated with the resource.
     *
     * @return string
     */
    public function getDefaultModel(): string;

    /**
     * check if stream enabled or not.
     *
     * @return bool
     */
    public function getDefaultStream(): bool;
}

<?php

namespace DeepSeek\Enums\Requests;

enum EndpointSuffixes: string
{
    case CHAT = '/chat/completions';
    case MODELS_LIST = '/models';
}

<?php

namespace DeepSeek\Enums\Configs;

enum TemperatureValues: string
{
    case CODING = "0.0";
    case MATH = "0.1";
    case DATA_ANALYSIS = "1.0";
    case DATA_CLEANING = "1.1";
    case GENERAL_CONVERSATION = "1.3";
    case TRANSLATION = "1.4";
    case CREATIVE_WRITING = "1.5";
    case POETRY = "1.6";
    case MAX_TOKENS = "4096";
    case RESPONSE_FORMAT_TYPE = "text";
}

<?php

namespace DeepSeek\Enums\Requests;

enum HeaderFlags: string
{
    case BASE_URL = 'base_uri';
    case TIMEOUT = 'timeout';
    case HEADERS = 'headers';
    case AUTHORIZATION = 'Authorization';
    case CONTENT_TYPE = 'content-type';
}

<?php

namespace DeepSeek\Enums\Queries;

enum QueryRoles: string
{
    case USER = 'user';
    case SYSTEM = 'system';
    case ASSISTANT = 'assistant';
    case TOOL = 'tool';
}

<?php

namespace DeepSeek\Enums\Data;

enum DataTypes: string
{
    case STRING = 'string';
    case INTEGER = 'integer';
    case FLOAT = 'float';
    case ARRAY = 'array';
    case OBJECT = 'object';
    case BOOL = 'bool';
    case JSON = 'json';
}

<?php

namespace DeepSeek\Enums\Requests;

enum ClientTypes: string
{
    case GUZZLE = 'guzzle';
    case SYMFONY = 'symfony';

}

<?php

namespace DeepSeek\Enums\Configs;

enum DefaultConfigs: string
{
    case BASE_URL = 'https://api.deepseek.com/v3';
    case MODEL = 'DeepSeek-R1';
    case TIMEOUT = '30';
    case STREAM = 'false';
}

<?php

namespace DeepSeek\Enums;

enum Models: string
{
    case CHAT = 'deepseek-chat';
    case CODER = 'deepseek-coder';
    case R1 = 'DeepSeek-R1';
    case R1Zero = 'DeepSeek-R1-Zero';
}

<?php

namespace DeepSeek\Enums\Requests;

enum HTTPState: int
{
    /**
     * HTTP successful response
     * status is ok
     */
    case OK = 200;

    /**
     * HTTP client error response
     * status is unauthorized
     */
    case UNAUTHORIZED = 401;

    /**
     * HTTP client error response
     * status is payment required
     */
    case PAYMENT_REQUIRED = 402;

}

<?php

declare(strict_types=1);

namespace DeepSeek\Models;

class BadResult extends ResultAbstract
{

}

<?php

declare(strict_types=1);

namespace DeepSeek\Models;

class SuccessResult extends ResultAbstract
{

}

<?php

declare(strict_types=1);

namespace DeepSeek\Models;

class FailureResult extends ResultAbstract
{

}

<?php

declare(strict_types=1);

namespace DeepSeek\Resources;

class Chat extends Resource
{

}

class
DeepSeekClient
implements ClientContract
{
protected ClientInterface $httpClient;
protected array $queries = [];
protected ?string $model;
protected bool $stream;
protected float $temperature;
protected int $maxTokens;
protected string $responseFormatType;
protected ResultContract $result;
protected string $requestMethod;
protected ?string $endpointSuffixes;
protected ?array $tools;
public function __construct (ClientInterface $httpClient) {}
public function run () : string {}
public static function build (string $apiKey, ?string $baseUrl = null, ?int $timeout = null, ?string $clientType = null) : self {}
public function query (string $content, ?string $role = "user") : self {}
public function resetQueries () {}
public function getModelsList () : self {}
public function withModel (?string $model = null) : self {}
public function withStream (bool $stream = true) : self {}
public function setTemperature (float $temperature) : self {}
public function setMaxTokens (int $maxTokens) : self {}
public function setResponseFormat (string $type) : self {}
public function buildQuery (string $content, ?string $role = null) : array {}
public function setResult (ResultContract $result) {}
public function getResult () : ResultContract {}
}

This code segment functions to: Provide a configurable interface for interacting with an API service by managing request parameters, handling authentication setup, processing data transformations, and orchestrating communication between different service components while maintaining state consistency across operations.

```php
[TASK_BEGIN]
class DeepSeekClient implements ClientContract
{
    use HasChat, HasCoder, HasToolsFunctionCalling;

    protected ClientInterface $httpClient;
    protected array $queries = [];
    protected ?string $model = null;
    protected bool $stream = false;
    protected float $temperature = TemperatureValues::DEFAULT;
    protected int $maxTokens = 0;
    protected string $responseFormatType = 'text';
    protected ResultContract $result;
    protected string $requestMethod = 'POST';
    protected ?string $endpointSuffixes = null;
    protected ?array $tools = null;

    public function __construct(ClientInterface $httpClient)
    {
        $this->httpClient = $httpClient;
    }

    public function run(): string
    {
        $apiRequest = ApiFactory::createRequest(
            $this->requestMethod,
            $this->endpointSuffixes ? EndpointSuffixes::value($this->endpointSuffixes) : null,
            $this->buildRequestData()
        );

        return $this->httpClient->sendRequest($apiRequest)->getBody()->getContents();
    }

    public static function build(string $apiKey, ?string $baseUrl = null, ?int $timeout = null, ?string $clientType = null): self
    {
        return new self(ApiFactory::create($apiKey, $baseUrl, $timeout, $clientType ? ClientTypes::value($clientType) : null));
    }

    public function query(string $content, ?string $role = "user"): self
    {
        $this->queries[] = $this->buildQuery($content, $role);
        return $this;
    }

    public function resetQueries()
    {
        $this->queries = [];
    }

    public function getModelsList(): self
    {
        $this->endpointSuffixes = EndpointSuffixes::MODELS_LIST;
        $this->requestMethod = 'GET';
        return $this;
    }

    public function withModel(?string $model = null): self
    {
        $this->model = $model;
        return $this;
    }

    public function withStream(bool $stream = true): self
    {
        $this->stream = $stream;
        return $this;
    }

    public function setTemperature(float $temperature): self
    {
        $this->temperature = $temperature;
        return $this;
    }

    public function setMaxTokens(int $maxTokens): self
    {
        $this->maxTokens = $maxTokens;
        return $this;
    }

    public function setResponseFormat(string $type): self
    {
        $this->responseFormatType = $type;
        return $this;
    }

    public function buildQuery(string $content, ?string $role = null): array
    {
        return [
            'role' => QueryRoles::tryFrom($role ?? QueryRoles::USER->value) ?? QueryRoles::USER,
            'content' => $content
        ];
    }

    public function setResult(ResultContract $result)
    {
        $this->result = $result;
    }

    public function getResult(): ResultContract
    {
        return $this->result;
    }

    private function buildRequestData(): array
    {
        return array_filter([
            'model' => $this->model,
            'messages' => $this->queries,
            'stream' => $this->stream,
            'temperature' => $this->temperature,
            'max_tokens' => $this->maxTokens,
            'response_format' => ['type' => $this->responseFormatType],
            'tools' => $this->tools
        ], fn($value) => !is_null($value) && $value !== '');
    }
}
[TASK_END]


<?php

namespace DeepSeek\Factories;

use DeepSeek\Contracts\Factories\ApiFactoryContract;
use DeepSeek\Enums\Configs\DefaultConfigs;
use DeepSeek\Enums\Requests\ClientTypes;
use DeepSeek\Enums\Requests\HeaderFlags;
use GuzzleHttp\Client;
use Psr\Http\Client\ClientInterface;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\Psr18Client;
use RuntimeException;
use InvalidArgumentException;

final class ApiFactory implements ApiFactoryContract
{
    protected string $apiKey;
    protected string $baseUrl;
    protected int $timeout;
    protected array $clientConfig;

    public static function build(): self
    {
        return new self();
    }

    public function setBaseUri(?string $baseUrl = null): self
    {
        $this->baseUrl = $baseUrl ? trim($baseUrl) : DefaultConfigs::BASE_URL->value;
        return $this;
    }

    public function setKey(string $apiKey): self
    {
        $this->apiKey = trim($apiKey);
        return $this;
    }

    public function setTimeout(?int $timeout = null): self
    {
        $this->timeout = $timeout ?: (int)DefaultConfigs::TIMEOUT->value;
        return $this;
    }

    public function initialize(): self
    {
        if (!isset($this->baseUrl)) {
            $this->setBaseUri();
        }

        if (!isset($this->apiKey)) {
            throw new RuntimeException('API key must be set using setKey() before initialization.');
        }

        if (!isset($this->timeout)) {
            $this->setTimeout();
        }

        $this->clientConfig = [
            HeaderFlags::BASE_URL->value => $this->baseUrl,
            HeaderFlags::TIMEOUT->value  => $this->timeout,
            HeaderFlags::HEADERS->value  => [
                HeaderFlags::AUTHORIZATION->value => 'Bearer ' . $this->apiKey,
                HeaderFlags::CONTENT_TYPE->value  => 'application/json',
            ],
        ];

        return $this;
    }

    public function run(?string $clientType = null): ClientInterface
    {
        $clientType = $clientType ?? ClientTypes::GUZZLE->value;

        if (!isset($this->clientConfig)) {
            $this->initialize();
        }

        return match (strtolower($clientType)) {
            ClientTypes::GUZZLE->value => new Client($this->clientConfig),
            ClientTypes::SYMFONY->value => new Psr18Client(HttpClient::create($this->clientConfig)),
            default => throw new InvalidArgumentException("Unsupported client type: {$clientType}")
        };
    }
}

<?php

declare(strict_types=1);

namespace DeepSeek\Resources;

use DeepSeek\Contracts\Models\ResultContract;
use DeepSeek\Contracts\Resources\ResourceContract;
use DeepSeek\Enums\Configs\DefaultConfigs;
use DeepSeek\Enums\Models;
use DeepSeek\Enums\Data\DataTypes;
use DeepSeek\Enums\Requests\EndpointSuffixes;
use DeepSeek\Enums\Requests\QueryFlags;
use DeepSeek\Models\BadResult;
use DeepSeek\Models\FailureResult;
use DeepSeek\Models\SuccessResult;
use DeepSeek\Traits\Queries\HasQueryParams;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Nyholm\Psr7\Factory\Psr17Factory;

class Resource implements ResourceContract
{
    use HasQueryParams;

    protected ClientInterface $client;
    protected ?string $endpointSuffixes;
    protected RequestFactoryInterface $requestFactory;
    protected StreamFactoryInterface $streamFactory;

    public function __construct(
        ClientInterface $client,
        ?string $endpointSuffixes = null,
        ?RequestFactoryInterface $requestFactory = null,
        ?StreamFactoryInterface $streamFactory = null
    ) {
        $this->client = $client;
        $this->endpointSuffixes = $endpointSuffixes ?: EndpointSuffixes::CHAT->value;
        $this->requestFactory = $requestFactory ?: new Psr17Factory();
        $this->streamFactory = $streamFactory ?: new Psr17Factory();
    }

    public function sendRequest(array $requestData, ?string $requestMethod = 'POST'): ResultContract
    {
        try {
            $request = $this->requestFactory->createRequest(
                $requestMethod,
                $this->getEndpointSuffix()
            );

            if ($requestMethod === 'POST') {
                $request = $request
                    ->withHeader('Content-Type', 'application/json')
                    ->withBody(
                        $this->streamFactory->createStream(
                            json_encode($this->resolveHeaders($requestData))
                        ));
            }

            $response = $this->client->sendRequest($request);

            return (new SuccessResult())->setResponse($response);
        } catch (BadResponseException $badResponse) {
            return (new BadResult())->setResponse($badResponse->getResponse());
        } catch (GuzzleException $error) {
            return new FailureResult($error->getCode(), $error->getMessage());
        } catch (\Exception $error) {
            return new FailureResult($error->getCode(), '{"error":"'.$error->getMessage().'"}');
        }
    }

    /**
     * Merge request data with default headers.
     *
     * This method merges the given query data with custom headers that are
     * prepared for the request.
     *
     * @param array $requestData The data to send in the request.
     * @return array The merged request data with default headers.
     */
    protected function resolveHeaders(array $requestData): array
    {
        return array_merge($requestData, $this->prepareCustomHeaderParams($requestData));
    }

    /**
     * Prepare the custom headers for the request.
     *
     * This method loops through the query parameters and applies the appropriate
     * type conversion before returning the final headers.
     *
     * @param array $query The data to send in the request.
     * @return array The custom headers for the request.
     */
    public function prepareCustomHeaderParams(array $query): array
    {
        $headers = [];
        $params = $this->getAllowedQueryParamsList();

        // Loop through the parameters and apply the conversion logic dynamically
        foreach ($params as $key => $type) {
            $headers[$key] = $this->getQueryParam($query, $key, $this->getDefaultForKey($key), $type);
        }

        return $headers;
    }

    /**
     * Get the endpoint suffix for the resource.
     *
     * This method returns the endpoint suffix that is used in the API URL.
     *
     * @return string The endpoint suffix.
     */
    public function getEndpointSuffix(): string
    {
        return $this->endpointSuffixes;
    }

    /**
     * Get the model associated with the resource.
     *
     * This method returns the default model value associated with the resource.
     *
     * @return string The default model value.
     */
    public function getDefaultModel(): string
    {
        return Models::CHAT->value;
    }

    /**
     * Check if stream is enabled or not.
     *
     * This method checks whether the streaming option is enabled based on the
     * default configuration.
     *
     * @return bool True if streaming is enabled, false otherwise.
     */
    public function getDefaultStream(): bool
    {
        return DefaultConfigs::STREAM->value === 'true';
    }

    /**
     * Get the list of query parameters and their corresponding types for conversion.
     *
     * This method returns an array of query keys and their associated data types
     * for use in preparing the custom headers.
     *
     * @return array An associative array of query keys and their data types.
     */
    protected function getAllowedQueryParamsList(): array
    {
        return [
            QueryFlags::MODEL->value => DataTypes::STRING->value,
            QueryFlags::STREAM->value => DataTypes::BOOL->value,
        ];
    }
}

<?php

namespace DeepSeek\Traits\Client;

use DeepSeek\Enums\Queries\QueryRoles;

trait HasToolsFunctionCalling
{
    /**
     * @param array $tools A list of tools the model may call.
     * @return self The current instance for method chaining.
     */
    public function setTools(array $tools): self
    {
        $this->tools = $tools;
        return $this;
    }

    /**
     * Add a query tool calls to the accumulated queries list.
     *
     * @param array $toolCalls The tool calls generated by the model, such as function calls.
     * @param string $content
     * @param string|null $role
     * @return self The current instance for method chaining.
     */
    public function queryToolCall(array $toolCalls, string $content, ?string $role = null): self
    {
        $this->queries[] = $this->buildToolCallQuery($toolCalls, $content, $role);
        return $this;
    }

    public function buildToolCallQuery(array $toolCalls, string $content, ?string $role = null): array
    {
        $query = [
            'role' => $role ?: QueryRoles::ASSISTANT->value,
            'tool_calls' => $toolCalls,
            'content' => $content,
        ];
        return $query;
    }

    /**
     * Add a query tool to the accumulated queries list.
     *
     * @param string $toolCallId
     * @param string $content
     * @param string|null $role
     * @return self The current instance for method chaining.
     */
    public function queryTool(string $toolCallId, string $content , ?string $role = null): self
    {
        $this->queries[] = $this->buildToolQuery($toolCallId, $content, $role);
        return $this;
    }

    public function buildToolQuery(string $toolCallId, string $content, ?string $role): array
    {
        $query = [
            'role' => $role ?: QueryRoles::TOOL->value,
            'tool_call_id' => $toolCallId,
            'content' => $content,
        ];
        return $query;
    }
}

<?php

declare(strict_types=1);

namespace DeepSeek\Models;

use DeepSeek\Contracts\Models\ResultContract;
use DeepSeek\Enums\Requests\HTTPState;
use Psr\Http\Message\ResponseInterface;

abstract class ResultAbstract implements ResultContract
{
    protected ?int $statusCode;
    protected ?string $content;
    /**
     * handel response coming from request
     * @var ResponseInterface|null
     */
    protected ?ResponseInterface $response;
    public function __construct(?int $statusCode = null, ?string $content = null)
    {
        $this->statusCode = $statusCode;
        $this->content = $content;
    }
    protected function setStatusCode(int $statusCode)
    {
        $this->statusCode = $statusCode;
    }
    public function getStatusCode(): int
    {
        return $this->statusCode;
    }
    protected function setContent(string $content): void
    {
        $this->content = $content;
    }
    public function getContent(): string
    {
        return $this->content;
    }
    public function setResponse(ResponseInterface $response): static
    {
        $this->response = $response;
        $this->setStatusCode($this->getResponse()->getStatusCode());
        $this->setContent($this->getResponse()->getBody()->getContents());
        return $this;
    }
    public function getResponse(): ResponseInterface
    {
        return $this->response;
    }
    public function isSuccess(): bool
    {
        return ($this->getStatusCode() === HTTPState::OK->value);
    }
}

<?php

namespace DeepSeek\Traits\Queries;

use DeepSeek\Enums\Data\DataTypes;
use DeepSeek\Enums\Requests\QueryFlags;

trait HasQueryParams
{
    /**
     * Helper method to get the query parameter or default value with type conversion.
     *
     * @param array $query
     * @param string $key
     * @param mixed $default
     * @param string $type
     * @return mixed
     */
    private function getQueryParam(array $query, string $key, $default, string $type): mixed
    {
        if (isset($query[$key])) {
            $value = $query[$key];
            return $this->convertValue($value, $type);
        }

        return $default;
    }

    /**
     * Convert the value to the specified type.
     *
     * @param mixed $value
     * @param string $type
     * @return mixed
     */
    private function convertValue($value, string $type): mixed
    {
        return match ($type) {
            DataTypes::STRING->value => (string)$value,
            DataTypes::INTEGER->value => (int)$value,
            DataTypes::FLOAT->value => (float)$value,
            DataTypes::ARRAY->value => (array)$value,
            DataTypes::OBJECT->value => (object)$value,
            DataTypes::BOOL->value => (bool)$value,
            DataTypes::JSON->value => json_decode((string)$value, true),
            default => $value,
        };
    }

    /**
     * Get default value for specific query keys.
     *
     * @param string $key
     * @return mixed
     */
    private function getDefaultForKey(string $key): mixed
    {
        return match ($key) {
            QueryFlags::MODEL->value => $this->getDefaultModel(),
            QueryFlags::STREAM->value => $this->getDefaultStream(),
            default => null,
        };
    }
}

<?php

namespace DeepSeek\Traits\Resources;

use DeepSeek\Resources\Chat;

trait HasChat
{
    /**
     * Send the accumulated queries to the Chat resource.
     *
     * @return string
     */
    public function chat(): string
    {
        $requestData = [
            'messages' => $this->queries,
            'model' => $this->model,
            'stream' => $this->stream,
        ];
        $this->queries = [];
        $this->setResult((new Chat($this->httpClient))->sendRequest($requestData));
        return $this->getResult()->getContent();
    }
}

<?php

namespace DeepSeek\Traits\Resources;

use DeepSeek\Resources\Coder;

trait HasCoder
{
    /**
     * Send the accumulated queries to the code resource.
     *
     * @return string
     */
    public function code(): string
    {
        $requestData = [
            'messages' => $this->queries,
            'model' => $this->model,
            'stream' => $this->stream,
        ];
        $this->queries = [];
        $this->setResult((new Coder($this->httpClient))->sendRequest($requestData));
        return $this->getResult()->getContent();
    }
}

<?php

namespace DeepSeek\Contracts;

interface ClientContract
{
    public function run(): string;
    public static function build(string $apiKey, ?string $baseUrl = null, ?int $timeout = null): self;
    public function query(string $content, ?string $role = "user"): self;
    public function getModelsList(): self;
    public function withModel(?string $model = null): self;
    public function withStream(bool $stream = true): self;
    public function buildQuery(string $content, ?string $role = null): array;
}

<?php

namespace DeepSeek\Contracts\Factories;

use DeepSeek\Factories\ApiFactory;
use GuzzleHttp\Client;
use Psr\Http\Client\ClientInterface;

interface ApiFactoryContract
{
    /**
     * Create a new instance of the factory.
     *
     * @return ApiFactory
     */
    public static function build(): ApiFactory;

    /**
     * Set the base URL for the API.
     *
     * @param string|null $baseUrl The base URL to set (optional).
     * @return ApiFactory
     */
    public function setBaseUri(?string $baseUrl = null): ApiFactory;

    /**
     * Set the API key for authentication.
     *
     * @param string $apiKey The API key to set.
     * @return ApiFactory
     */
    public function setKey(string $apiKey): ApiFactory;

    /**
     * Set the timeout for the API request.
     *
     * @param int|null $timeout The timeout value in seconds (optional).
     * @return ApiFactory
     */
    public function setTimeout(?int $timeout = null): ApiFactory;

    /**
     * Build and return http Client instance.
     *
     * @return ClientInterface
     */
    public function run(?string $clientType = null): ClientInterface;
}

<?php

declare(strict_types=1);

namespace DeepSeek\Resources;

use DeepSeek\Enums\Models;

class Coder extends Resource
{
    /**
     * Get the model associated with the resource.
     *
     * This method returns the default model value associated with the resource.
     *
     * @return string The default model value.
     */
    public function getDefaultModel(): string
    {
        return Models::CODER->value;
    }
}

<?php

namespace DeepSeek\Enums\Requests;

enum QueryFlags: string
{
    case MESSAGES = 'messages';
    case MODEL = 'model';
    case STREAM = 'stream';
    case TEMPERATURE = 'temperature';
    case MAX_TOKENS = 'max_tokens';
    case TOOLS = 'tools';
    case RESPONSE_FORMAT = 'response_format';
}

<?php

namespace DeepSeek\Contracts\Models;

interface ResultContract
{
    /**
     * result status code
     * @return int
     */
    public function getStatusCode(): int;

    /**
     * result content date as a string
     * @return string
     */
    public function getContent(): string;

    /**
     * if response status code is ok (200)
     * @return bool
     */
    public function isSuccess(): bool;
}

<?php

namespace DeepSeek\Contracts\Resources;

/**
 * Interface for defining the structure of resource classes.
 */
interface ResourceContract
{
    /**
     * Get the endpoint suffix for the resource.
     *
     * @return string
     */
    public function getEndpointSuffix(): string;

    /**
     * Get the model associated with the resource.
     *
     * @return string
     */
    public function getDefaultModel(): string;

    /**
     * check if stream enabled or not.
     *
     * @return bool
     */
    public function getDefaultStream(): bool;
}

<?php

namespace DeepSeek\Enums\Requests;

enum EndpointSuffixes: string
{
    case CHAT = '/chat/completions';
    case MODELS_LIST = '/models';
}

<?php

namespace DeepSeek\Enums\Configs;

enum TemperatureValues: string
{
    case CODING = "0.0";
    case MATH = "0.1";
    case DATA_ANALYSIS = "1.0";
    case DATA_CLEANING = "1.1";
    case GENERAL_CONVERSATION = "1.3";
    case TRANSLATION = "1.4";
    case CREATIVE_WRITING = "1.5";
    case POETRY = "1.6";
    case MAX_TOKENS = "4096";
    case RESPONSE_FORMAT_TYPE = "text";
}

<?php

namespace DeepSeek\Enums\Requests;

enum HeaderFlags: string
{
    case BASE_URL = 'base_uri';
    case TIMEOUT = 'timeout';
    case HEADERS = 'headers';
    case AUTHORIZATION = 'Authorization';
    case CONTENT_TYPE = 'content-type';
}

<?php

namespace DeepSeek\Enums\Queries;

enum QueryRoles: string
{
    case USER = 'user';
    case SYSTEM = 'system';
    case ASSISTANT = 'assistant';
    case TOOL = 'tool';
}

<?php

namespace DeepSeek\Enums\Data;

enum DataTypes: string
{
    case STRING = 'string';
    case INTEGER = 'integer';
    case FLOAT = 'float';
    case ARRAY = 'array';
    case OBJECT = 'object';
    case BOOL = 'bool';
    case JSON = 'json';
}

<?php

namespace DeepSeek\Enums\Requests;

enum ClientTypes: string
{
    case GUZZLE = 'guzzle';
    case SYMFONY = 'symfony';

}

<?php

namespace DeepSeek\Enums\Configs;

enum DefaultConfigs: string
{
    case BASE_URL = 'https://api.deepseek.com/v3';
    case MODEL = 'DeepSeek-R1';
    case TIMEOUT = '30';
    case STREAM = 'false';
}

<?php

namespace DeepSeek\Enums;

enum Models: string
{
    case CHAT = 'deepseek-chat';
    case CODER = 'deepseek-coder';
    case R1 = 'DeepSeek-R1';
    case R1Zero = 'DeepSeek-R1-Zero';
}

<?php

namespace DeepSeek\Enums\Requests;

enum HTTPState: int
{
    /**
     * HTTP successful response
     * status is ok
     */
    case OK = 200;

    /**
     * HTTP client error response
     * status is unauthorized
     */
    case UNAUTHORIZED = 401;

    /**
     * HTTP client error response
     * status is payment required
     */
    case PAYMENT_REQUIRED = 402;

}

<?php

declare(strict_types=1);

namespace DeepSeek\Models;

class BadResult extends ResultAbstract
{

}

<?php

declare(strict_types=1);

namespace DeepSeek\Models;

class SuccessResult extends ResultAbstract
{

}

<?php

declare(strict_types=1);

namespace DeepSeek\Models;

class FailureResult extends ResultAbstract
{

}

<?php

declare(strict_types=1);

namespace DeepSeek\Resources;

class Chat extends Resource
{

}

This code segment functions to: Provide a configurable interface for constructing API requests, handling parameter settings and query building while integrating multiple service capabilities through trait composition, and execute network operations through a centralized resource handler.

```php
[TASK_BEGIN]
class DeepSeekClient implements ClientContract
{
    protected ClientInterface $httpClient;
    protected array $queries = [];
    protected ?string $model = null;
    protected bool $stream = false;
    protected float $temperature = TemperatureValues::DEFAULT;
    protected int $maxTokens = 1024;
    protected string $responseFormatType = 'json';
    protected ResultContract $result;
    protected string $requestMethod = 'POST';
    protected ?string $endpointSuffixes = null;
    protected ?array $tools = null;

    public function __construct(ClientInterface $httpClient)
    {
        $this->httpClient = $httpClient;
    }

    public function run(): string
    {
        $request = $this->httpClient->createRequest(
            $this->requestMethod,
            $this->endpointSuffixes ? EndpointSuffixes::from($this->endpointSuffixes)->value : '',
            ['body' => json_encode($this->buildRequestBody())]
        );

        $response = $this->httpClient->sendRequest($request);
        $this->result->setResponse($response);

        return $this->result->getAnswer();
    }

    public static function build(string $apiKey, ?string $baseUrl = null, ?int $timeout = null, ?string $clientType = null): self
    {
        return ApiFactory::create(
            $apiKey,
            $baseUrl,
            $timeout,
            $clientType ? ClientTypes::from($clientType) : ClientTypes::CHAT
        );
    }

    public function query(string $content, ?string $role = "user"): self
    {
        $this->queries[] = $this->buildQuery($content, $role);
        return $this;
    }

    public function resetQueries()
    {
        $this->queries = [];
    }

    public function getModelsList(): self
    {
        $this->endpointSuffixes = EndpointSuffixes::MODELS_LIST;
        $this->requestMethod = 'GET';
        return $this;
    }

    public function withModel(?string $model = null): self
    {
        $this->model = $model;
        return $this;
    }

    public function withStream(bool $stream = true): self
    {
        $this->stream = $stream;
        return $this;
    }

    public function setTemperature(float $temperature): self
    {
        $this->temperature = $temperature;
        return $this;
    }

    public function setMaxTokens(int $maxTokens): self
    {
        $this->maxTokens = $maxTokens;
        return $this;
    }

    public function setResponseFormat(string $type): self
    {
        $this->responseFormatType = $type;
        return $this;
    }

    public function buildQuery(string $content, ?string $role = null): array
    {
        return [
            'role' => QueryRoles::tryFrom($role ?? QueryRoles::USER)->value,
            'content' => $content
        ];
    }

    public function setResult(ResultContract $result)
    {
        $this->result = $result;
    }

    public function getResult(): ResultContract
    {
        return $this->result;
    }

    private function buildRequestBody(): array
    {
        return array_filter([
            'model' => $this->model,
            'messages' => $this->queries,
            'stream' => $this->stream,
            'temperature' => $this->temperature,
            'max_tokens' => $this->maxTokens,
            'response_format' => ['type' => $this->responseFormatType],
            'tools' => $this->tools
        ], fn($v) => $v !== null);
    }
}
[TASK_END]
```