44
55namespace Elegantly \Translator \Services \Translate ;
66
7- use Closure ;
7+ use Elegantly \Translator \Services \AbstractOpenAiService ;
8+ use Illuminate \Support \Facades \Concurrency ;
89use InvalidArgumentException ;
910use OpenAI ;
1011
11- class OpenAiService implements TranslateServiceInterface
12+ class OpenAiService extends AbstractOpenAiService implements TranslateServiceInterface
1213{
1314 public function __construct (
1415 public \OpenAI \Client $ client ,
1516 public string $ model ,
1617 public string $ prompt ,
18+ public bool $ concurrency ,
19+ public int $ chunk ,
1720 ) {
1821 //
1922 }
@@ -24,6 +27,8 @@ public static function make(): self
2427 client: static ::makeClient (),
2528 model: config ('translator.translate.services.openai.model ' ),
2629 prompt: config ('translator.translate.services.openai.prompt ' ),
30+ concurrency: config ('translator.translate.services.openai.concurrency ' ) ?? true ,
31+ chunk: config ('translator.translate.services.openai.chunk ' ) ?? 10 ,
2732 );
2833 }
2934
@@ -53,62 +58,88 @@ public static function makeClient(): \OpenAI\Client
5358
5459 public static function getTimeout (): int
5560 {
56- return (int ) (config ('translator.translate.services.openai.request_timeout ' ) ?? config ( ' translator.services.openai.request_timeout ' ) ?? 120 );
61+ return (int ) (config ('translator.translate.services.openai.request_timeout ' ) ?? parent :: getTimeout () );
5762 }
5863
5964 /**
60- * @template TValue
61- *
62- * @param (Closure():TValue) $callback
63- * @return TValue
65+ * @param array<array-key, null|scalar> $texts
66+ * @return array<array-key, null|scalar>
6467 */
65- protected function withTemporaryTimeout ( int $ limit , Closure $ callback ): mixed
68+ public function translateAllWithConcurrency ( array $ texts , string $ targetLocale ): array
6669 {
67- $ initial = (int ) ini_get ('max_execution_time ' );
70+ $ model = $ this ->model ;
71+ $ prompt = $ this ->prompt ;
72+
73+ $ tasks = collect ($ texts )
74+ ->chunk ($ this ->chunk )
75+ ->map (function ($ chunk ) use ($ model , $ prompt , $ targetLocale ) {
76+
77+ return function () use ($ model , $ prompt , $ targetLocale , $ chunk ) {
78+ $ response = static ::makeClient ()->chat ()->create ([
79+ 'model ' => $ model ,
80+ 'response_format ' => ['type ' => 'json_object ' ],
81+ 'messages ' => [
82+ [
83+ 'role ' => 'system ' ,
84+ 'content ' => str_replace ('{targetLocale} ' , $ targetLocale , $ prompt ),
85+ ],
86+ [
87+ 'role ' => 'user ' ,
88+ 'content ' => $ chunk ->toJson (),
89+ ],
90+ ],
91+ ]);
6892
69- set_time_limit ($ limit );
93+ $ content = $ response ->choices [0 ]->message ->content ;
94+ $ content = str_replace ('\\\/ ' , "\/ " , $ content );
95+ $ translations = json_decode ($ content , true );
7096
71- try {
72- return $ callback ();
73- } catch (\Throwable $ th ) {
74- throw $ th ;
75- } finally {
76- set_time_limit ($ initial );
77- }
97+ return $ translations ;
98+ };
99+ })
100+ ->all ();
101+
102+ $ results = $ this ->withTemporaryTimeout (
103+ static ::getTimeout () * count ($ tasks ),
104+ fn () => Concurrency::run ($ tasks ),
105+ );
106+
107+ return collect ($ results )->collapse ()->toArray ();
78108 }
79109
80110 public function translateAll (array $ texts , string $ targetLocale ): array
81111 {
82- return $ this ->withTemporaryTimeout (
83- static ::getTimeout (),
84- function () use ($ texts , $ targetLocale ) {
85- return collect ($ texts )
86- ->chunk (50 )
87- ->map (function ($ chunk ) use ($ targetLocale ) {
88- $ response = $ this ->client ->chat ()->create ([
89- 'model ' => $ this ->model ,
90- 'response_format ' => ['type ' => 'json_object ' ],
91- 'messages ' => [
92- [
93- 'role ' => 'system ' ,
94- 'content ' => str_replace ('{targetLocale} ' , $ targetLocale , $ this ->prompt ),
95- ],
96- [
97- 'role ' => 'user ' ,
98- 'content ' => $ chunk ->toJson (),
99- ],
100- ],
101- ]);
112+ if ($ this ->concurrency ) {
113+ return $ this ->translateAllWithConcurrency ($ texts , $ targetLocale );
114+ }
102115
103- $ content = $ response ->choices [0 ]->message ->content ;
104- $ content = str_replace ('\\\/ ' , "\/ " , $ content );
105- $ translations = json_decode ($ content , true );
116+ $ chunks = collect ($ texts )->chunk ($ this ->chunk );
106117
107- return $ translations ;
108- })
109- ->collapse ()
110- ->toArray ();
111- }
118+ return $ this ->withTemporaryTimeout (
119+ static ::getTimeout () * count ($ chunks ),
120+ fn () => $ chunks ->map (function ($ chunk ) use ($ targetLocale ) {
121+
122+ $ response = $ this ->client ->chat ()->create ([
123+ 'model ' => $ this ->model ,
124+ 'response_format ' => ['type ' => 'json_object ' ],
125+ 'messages ' => [
126+ [
127+ 'role ' => 'system ' ,
128+ 'content ' => str_replace ('{targetLocale} ' , $ targetLocale , $ this ->prompt ),
129+ ],
130+ [
131+ 'role ' => 'user ' ,
132+ 'content ' => $ chunk ->toJson (),
133+ ],
134+ ],
135+ ]);
136+
137+ $ content = $ response ->choices [0 ]->message ->content ;
138+ $ content = str_replace ('\\\/ ' , "\/ " , $ content );
139+ $ translations = json_decode ($ content , true );
140+
141+ return $ translations ;
142+ })->collapse ()->toArray ()
112143 );
113144
114145 }
0 commit comments