PluginProbe
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 3.7.5
AI Engine – The Chatbot, AI Framework & MCP for WordPress v3.7.5
3.7.7 3.7.6 3.7.5 3.7.4 3.7.3 3.7.2 3.7.1 3.7.0 3.6.9 3.6.8 3.6.7 3.6.6 3.6.4 3.6.5 3.6.3 3.6.2 3.6.1 3.6.0 3.5.9 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 All 527 releases
ai-engine / vendor / yethee / tiktoken / src / EncoderProvider.php
EncoderProvider.php
168 lines 5.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Yethee\Tiktoken;
6
7 use InvalidArgumentException;
8 use Symfony\Contracts\Service\ResetInterface;
9 use Yethee\Tiktoken\Vocab\Loader\DefaultVocabLoader;
10 use Yethee\Tiktoken\Vocab\Vocab;
11 use Yethee\Tiktoken\Vocab\VocabLoader;
12
13 use function getenv;
14 use function sprintf;
15 use function str_starts_with;
16 use function sys_get_temp_dir;
17
18 use const DIRECTORY_SEPARATOR;
19
20 final class EncoderProvider implements ResetInterface
21 {
22 public const ENCODINGS = [
23 'r50k_base' => [
24 'vocab' => 'https://openaipublic.blob.core.windows.net/encodings/r50k_base.tiktoken',
25 'pat' => '/\'s|\'t|\'re|\'ve|\'m|\'ll|\'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/u',
26 ],
27 'p50k_base' => [
28 'vocab' => 'https://openaipublic.blob.core.windows.net/encodings/p50k_base.tiktoken',
29 'pat' => '/\'s|\'t|\'re|\'ve|\'m|\'ll|\'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/u',
30 ],
31 'p50k_edit' => [
32 'vocab' => 'https://openaipublic.blob.core.windows.net/encodings/p50k_base.tiktoken',
33 'pat' => '/\'s|\'t|\'re|\'ve|\'m|\'ll|\'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/u',
34 ],
35 'cl100k_base' => [
36 'vocab' => 'https://openaipublic.blob.core.windows.net/encodings/cl100k_base.tiktoken',
37 'pat' => '/(?i:\'s|\'t|\'re|\'ve|\'m|\'ll|\'d)|[^\r\n\p{L}\p{N}]?\p{L}+|\p{N}{1,3}| ?[^\s\p{L}\p{N}]+[\r\n]*|\s*[\r\n]+|\s+(?!\S)|\s+/u',
38 ],
39 ];
40 private const MODEL_PREFIX_TO_ENCODING = [
41 'gpt-4-' => 'cl100k_base',
42 'gpt-3.5-turbo-' => 'cl100k_base',
43 ];
44 private const MODEL_TO_ENCODING = [
45 'gpt-4' => 'cl100k_base',
46 'gpt-3.5-turbo' => 'cl100k_base',
47 'text-davinci-003' => 'p50k_base',
48 'text-davinci-002' => 'p50k_base',
49 'text-davinci-001' => 'r50k_base',
50 'text-curie-001' => 'r50k_base',
51 'text-babbage-001' => 'r50k_base',
52 'text-ada-001' => 'r50k_base',
53 'davinci' => 'r50k_base',
54 'curie' => 'r50k_base',
55 'babbage' => 'r50k_base',
56 'ada' => 'r50k_base',
57 'code-davinci-002' => 'p50k_base',
58 'code-davinci-001' => 'p50k_base',
59 'code-cushman-002' => 'p50k_base',
60 'code-cushman-001' => 'p50k_base',
61 'davinci-codex' => 'p50k_base',
62 'cushman-codex' => 'p50k_base',
63 'text-davinci-edit-001' => 'p50k_edit',
64 'code-davinci-edit-001' => 'p50k_edit',
65 'text-embedding-ada-002' => 'cl100k_base',
66 'text-similarity-davinci-001' => 'r50k_base',
67 'text-similarity-curie-001' => 'r50k_base',
68 'text-similarity-babbage-001' => 'r50k_base',
69 'text-similarity-ada-001' => 'r50k_base',
70 'text-search-davinci-doc-001' => 'r50k_base',
71 'text-search-curie-doc-001' => 'r50k_base',
72 'text-search-babbage-doc-001' => 'r50k_base',
73 'text-search-ada-doc-001' => 'r50k_base',
74 'code-search-babbage-code-001' => 'r50k_base',
75 'code-search-ada-code-001' => 'r50k_base',
76 ];
77
78 private VocabLoader|null $vocabLoader = null;
79 private string|null $vocabCacheDir;
80
81 /** @var array<non-empty-string, Encoder> */
82 private array $encoders = [];
83
84 /** @var array<string, Vocab> */
85 private array $vocabs = [];
86
87 public function __construct()
88 {
89 $cacheDir = getenv('TIKTOKEN_CACHE_DIR');
90
91 if ($cacheDir === false) {
92 $cacheDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'tiktoken';
93 }
94
95 $this->vocabCacheDir = $cacheDir !== '' ? $cacheDir : null;
96 }
97
98 /** @param non-empty-string $model */
99 public function getForModel(string $model): Encoder
100 {
101 if (isset(self::MODEL_TO_ENCODING[$model])) {
102 return $this->get(self::MODEL_TO_ENCODING[$model]);
103 }
104
105 foreach (self::MODEL_PREFIX_TO_ENCODING as $prefix => $modelEncoding) {
106 if (str_starts_with($model, $prefix)) {
107 return $this->get($modelEncoding);
108 }
109 }
110
111 throw new InvalidArgumentException(sprintf('Unknown model name: %s', $model));
112 }
113
114 /** @param non-empty-string $encodingName */
115 public function get(string $encodingName): Encoder
116 {
117 if (! isset(self::ENCODINGS[$encodingName])) {
118 throw new InvalidArgumentException(sprintf('Unknown encoding: %s', $encodingName));
119 }
120
121 if (! isset($this->encoders[$encodingName])) {
122 $options = self::ENCODINGS[$encodingName];
123
124 return $this->encoders[$encodingName] = new Encoder(
125 $encodingName,
126 $this->getVocab($encodingName),
127 $options['pat'],
128 );
129 }
130
131 return $this->encoders[$encodingName];
132 }
133
134 /** @param non-empty-string|null $cacheDir */
135 public function setVocabCache(string|null $cacheDir): void
136 {
137 $this->vocabCacheDir = $cacheDir;
138 $this->vocabLoader = null;
139 }
140
141 /** @psalm-api */
142 public function setVocabLoader(VocabLoader $loader): void
143 {
144 $this->vocabLoader = $loader;
145 }
146
147 public function reset(): void
148 {
149 $this->encoders = [];
150 $this->vocabs = [];
151 }
152
153 private function getVocab(string $encodingName): Vocab
154 {
155 if (isset($this->vocabs[$encodingName])) {
156 return $this->vocabs[$encodingName];
157 }
158
159 $loader = $this->vocabLoader;
160
161 if ($loader === null) {
162 $loader = $this->vocabLoader = new DefaultVocabLoader($this->vocabCacheDir);
163 }
164
165 return $this->vocabs[$encodingName] = $loader->load(self::ENCODINGS[$encodingName]['vocab']);
166 }
167 }
168