client = $client; $this->modelName = $config['model_name'] ?? $this->modelName; $this->temperature = $config['temperature'] ?? $this->temperature; $this->maxTokens = $config['max_tokens'] ?? $this->maxTokens; $this->topP = $config['top_p'] ?? $this->topP; $this->frequencyPenalty = $config['frequency_penalty'] ?? $this->frequencyPenalty; $this->presencePenalty = $config['presence_penalty'] ?? $this->presencePenalty; $this->n = $config['n'] ?? $this->n; $this->bestOf = $config['best_of'] ?? $this->bestOf; $this->modelKwargs = $config['model_kwargs'] ?? $this->modelKwargs; $this->batchSize = $config['batch_size'] ?? $this->batchSize; $this->requestTimeout = $config['request_timeout'] ?? $this->requestTimeout; $this->logitBias = $config['logit_bias'] ?? $this->logitBias; $this->maxRetries = $config['max_retries'] ?? $this->maxRetries; $this->streaming = $config['streaming'] ?? $this->streaming; $token = getenv('OPENAI_API_KEY'); if (!$token) { $token = ($config['openai_api_key'] ?? null); } $this->openaiApiKey = $token; if ($this->openaiApiKey === null) { throw new Exception('You have to provide an APIKEY.'); } if ($client === null) { $client = OpenAI::client($this->openaiApiKey); } $this->client = $client; } /** * Get the default parameters for this LLM. * * @return array */ public function defaultParams(): array { return [ 'model' => $this->modelName, 'temperature' => $this->temperature, 'max_tokens' => $this->maxTokens, 'top_p' => $this->topP, 'frequency_penalty' => $this->frequencyPenalty, 'presence_penalty' => $this->presencePenalty, 'n' => $this->n, 'best_of' => $this->bestOf, 'logit_bias' => $this->logitBias, ]; } /** * Call out to OpenAI's endpoint with k unique prompts. * * @param array $prompts The parameters to pass into the model. * @param ?array $stop The stop words to use when generating. * * @return LLMResult The full LLM output. */ public function generateResult(array $prompts, ?array $stop = null): LLMResult { // Call out to OpenAI's endpoint with k unique prompts. $params = $this->getInvocationParams(); $subPrompts = $this->getSubPrompts($params, $prompts, $stop); $choices = []; $tokenUsage = [ 'completion_tokens' => 0, 'prompt_tokens' => 0, 'total_tokens' => 0, ]; // Get the token usage from the response. // Includes prompt, completion, and total tokens used. $keys = ['completion_tokens', 'prompt_tokens', 'total_tokens']; foreach ($subPrompts as $_prompts) { $response = $this->completionWithRetry($this->client, ['prompt' => $_prompts], ...$params); $choices = array_merge($choices, $response['choices']); $this->updateTokenUsage($keys, $response, $tokenUsage); } return $this->createLlmResult($choices, $prompts, $tokenUsage); } private function completionWithRetry($client, $params, ...$additionalParams) { $params = array_merge($additionalParams, $params); $params['logit_bias'] = array_key_exists('logit_bias', $params) ? (object) $params['logit_bias'] : (object)[]; $backoff = new Backoff($this->maxRetries, 'exponential', 10000, true); $result = $backoff->run(function () use ($client, $params) { return $client->completions()->create($params); }); return $result->toArray(); } private function updateTokenUsage($keys, $response, &$tokenUsage) { // Update token usage. $keysTo_use = array_intersect($keys, array_keys($response['usage'])); foreach ($keysTo_use as $_key) { if (! array_key_exists($_key, $tokenUsage)) { $tokenUsage[$_key] = $response['usage'][$_key]; } else { $tokenUsage[$_key] += $response['usage'][$_key]; } } } /** * Get the sub prompts for llm call. * * @param array $params * @param array $prompts * @param ?array $stop * * @return array */ private function getSubPrompts(array &$params, array $prompts, ?array $stop = null) { /** Get the sub prompts for llm call. */ if ($stop !== null) { if (array_key_exists('stop', $params)) { throw new InvalidArgumentException('`stop` found in both the input and default params.'); } $params['stop'] = $stop; } if ($params['max_tokens'] == -1) { if (count($prompts) !== 1) { throw new InvalidArgumentException('max_tokens set to -1 not supported for multiple inputs.'); } $params['max_tokens'] = $this->max_tokens_for_prompt($prompts[0]); } $subPrompts = array_chunk($prompts, $this->batchSize); return $subPrompts; } private function createLlmResult($choices, $prompts, $tokenUsage) { $generations = []; foreach ($choices as $choice) { $generations[] = [ new Generation( $choice['text'], [ 'finish_reason' => $choice['finish_reason'] ?? null, 'logprobs' => $choice['logprobs'] ?? null, ], ) ]; } return new LLMResult($generations, ['token_usage' => $tokenUsage, 'model_name' => $this->modelName]); } /** Get the parameters used to invoke the model. */ public function getInvocationParams() { return $this->defaultParams(); } /** * Get the identifying parameters. * * @return array */ public function getIdentifyingParams(): array { $defaultParams = $this->defaultParams(); return array_merge(['model_name' => $this->modelName], $defaultParams); } public function llmType(): string { return 'openai'; } public function toArray(): array { return $this->getIdentifyingParams(); } }