1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
Scenarist.php
См. документацию.
1<?php
2declare(strict_types=1);
3
4namespace Bitrix\Landing\Copilot\Generation\Scenario;
5
6use Bitrix\AI\Limiter\Enums\TypeLimit;
7use Bitrix\Landing\Copilot\Connector;
8use Bitrix\Landing\Copilot\Connector\AI\RequestLimiter;
9use Bitrix\Landing\Copilot\Connector\Chat\ChatBotMessageDto;
10use Bitrix\Landing\Copilot\Generation;
11use Bitrix\Landing\Copilot\Generation\GenerationException;
12use Bitrix\Landing\Copilot\Generation\Type\GenerationErrors;
13use Bitrix\Landing\Copilot\Generation\Type\RequestQuotaDto;
14use Bitrix\Landing\Copilot\Generation\Type\ScenarioStepDto;
15use Bitrix\Landing\Copilot\Generation\Type\StepStatuses;
16use Bitrix\Landing\Copilot\Model\StepsTable;
17use Bitrix\Main\Loader;
18
20{
21 private const EVENT_STEP = 'onExecuteStep';
22
23 private IScenario $scenario;
24 private Generation $generation;
25
26 private ?int $stepId;
27
31 private array $steps;
32
36 private $onStepChangeCallback;
37
41 private $onSaveCallback;
42
46 private $onFinishCallback;
47
48 private RequestLimiter $requestLimiter;
49
50 public function __construct(IScenario $scenario, Generation $generation)
51 {
52 $this->scenario = $scenario;
53 $this->generation = $generation;
54
55 $this->stepId = $this->generation->getStep();
56
57 $this->initSteps();
58 }
59
60 private function initSteps(): void
61 {
62 foreach ($this->scenario->getMap() as $stepId => $step)
63 {
64 $scenarioStep = new ScenarioStepDto(
65 $stepId,
66 $step,
67 StepStatuses::New,
68 );
69 $this->steps[$stepId] = $scenarioStep;
70 }
71
72 $query = StepsTable::query()
73 ->setSelect(['ID', 'STEP_ID', 'STATUS'])
74 ->where('GENERATION_ID', $this->generation->getId())
75 ->exec()
76 ;
77 while ($step = $query->fetch())
78 {
79 if (
80 isset($this->steps[(int)$step['STEP_ID']])
81 && StepStatuses::tryFrom((int)$step['STATUS'])
82 )
83 {
84 $this->steps[(int)$step['STEP_ID']]->status =
85 StepStatuses::from((int)$step['STATUS']);
86 $this->steps[(int)$step['STEP_ID']]->entityId = (int)$step['ID'];
87 }
88 }
89 }
90
91 public function getStep(): ?int
92 {
93 return $this->stepId;
94 }
95
100 public function isFinished(): bool
101 {
102 foreach ($this->steps as $step)
103 {
104 if ($step->status !== StepStatuses::Finished)
105 {
106 return false;
107 }
108 }
109
110 return true;
111 }
112
117 public function finish(): void
118 {
119 foreach ($this->steps as $step)
120 {
121 if ($step->status !== StepStatuses::Finished)
122 {
123 $this->saveStepStatus($step, StepStatuses::Finished);
124 }
125 }
126 }
127
132 public function isError(): bool
133 {
134 foreach ($this->steps as $step)
135 {
136 if ($step->status === StepStatuses::Error)
137 {
138 return true;
139 }
140 }
141
142 return false;
143 }
144
149 public function clearErrors(): void
150 {
151 foreach ($this->steps as $step)
152 {
153 if (
154 $step->status->value > StepStatuses::New->value
155 && $step->status->value < StepStatuses::Finished->value
156 )
157 {
158 $step->step->init($this->generation, $step->stepId);
159 $step->step->clearErrors();
160 $this->saveStepStatus($step, StepStatuses::New);
161 }
162 }
163 }
164
165 public function onStepChange(callable $callback): self
166 {
167 $this->onStepChangeCallback = $callback;
168
169 return $this;
170 }
171
172 private function callOnStepChange(): void
173 {
174 if (isset($this->onStepChangeCallback) && is_int($this->stepId))
175 {
176 call_user_func($this->onStepChangeCallback, $this->stepId);
177 }
178 }
179
180 public function onSave(callable $callback): self
181 {
182 $this->onSaveCallback = $callback;
183
184 return $this;
185 }
186
187 private function callOnSave(): void
188 {
189 if (isset($this->onSaveCallback))
190 {
191 call_user_func($this->onSaveCallback);
192 }
193 }
194
195 public function onFinish(callable $callback): self
196 {
197 $this->onFinishCallback = $callback;
198
199 return $this;
200 }
201
202 private function callOnFinish(): void
203 {
204 if (isset($this->onFinishCallback))
205 {
206 call_user_func($this->onFinishCallback);
207 }
208 }
209
215 public function execute(): void
216 {
217 if (!$this->scenario->checkStep($this->stepId))
218 {
219 return;
220 }
221
222 $this->stepId = $this->stepId ?? $this->scenario->getFirstStepId();
223 if (!$this->stepId)
224 {
225 return;
226 }
227
228 foreach ($this->steps as $stepId => $step)
229 {
230 if ($stepId > $this->stepId)
231 {
232 break;
233 }
234
235 if (
236 $step->status === StepStatuses::Finished
237 || $step->status === StepStatuses::Error
238 )
239 {
240 continue;
241 }
242
243 try
244 {
245 $this->executeStep($step);
246 }
247 catch (GenerationException $e)
248 {
249 $this->saveStepStatus($step, StepStatuses::Error);
250 throw $e;
251 }
252
253 if (
254 $this->stepId === $stepId
255 && (
256 $step->step->isFinished()
257 || $step->step->isAsync()
258 )
259 )
260 {
261 $this->stepId = $this->scenario->getNextStepId($this->stepId);
262 if (!$this->stepId)
263 {
264 break;
265 }
266
267 $this->callOnStepChange();
268 $this->callOnSave();
269 }
270 }
271
272 if ($this->isFinished())
273 {
274 $this->scenario->onFinish($this->generation);
275 $this->callOnFinish();
276 }
277 }
278
285 private function executeStep(ScenarioStepDto $step): void
286 {
287 $step->step->init($this->generation, $step->stepId);
288
289 if (
290 !$step->step->isStarted()
291 && $step->stepId === $this->scenario->getQuotaCalculateStep()
292 )
293 {
294 $this->checkRequestQuotas();
295 }
296
297 $step->step->execute();
298
299 if ($step->step->isStarted())
300 {
301 $this->saveStepStatus($step, StepStatuses::Started);
302 }
303
304 if ($step->step->isFinished())
305 {
306 $this->saveStepStatus($step, StepStatuses::Finished);
307 }
308
309 if ($step->step->isChanged() || $step->step->isFinished())
310 {
311 $this->callOnSave();
312 }
313
314 $this->generation->getEvent()->send(self::EVENT_STEP);
315 }
316
317 private function saveStepStatus(ScenarioStepDto $step, StepStatuses $status): void
318 {
319 $step->status = $status;
320
321 if (!isset($step->entityId))
322 {
323 $resAdd = StepsTable::add([
324 'GENERATION_ID' => $this->generation->getId(),
325 'STEP_ID' => $step->stepId,
326 'CLASS' => $step->step::class,
327 'STATUS' => $status->value,
328 ]);
329
330 if ($resAdd->isSuccess())
331 {
332 $step->entityId = $resAdd->getId();
333 }
334
335 return;
336 }
337
338 StepsTable::update($step->entityId, [
339 'STATUS' => $status->value,
340 ]);
341 }
342
347 private function checkRequestQuotas(): void
348 {
349 $quotaLimitText = $this->getQuotaLimitText();
350 if (is_string($quotaLimitText))
351 {
352 throw new GenerationException(
353 GenerationErrors::requestQuotaExceeded,
354 $quotaLimitText,
355 [
356 'errorText' => $quotaLimitText,
357 ]
358 );
359 }
360
361 if (
362 !$this->generation->getChatId()
363 || $this->generation->getChatId() <= 0
364 )
365 {
366 return;
367 }
368
369 $this->generation->getScenario()?->getChatbot()?->onRequestQuotaOk(
370 new ChatBotMessageDto(
371 $this->generation->getChatId(),
372 $this->generation->getId(),
373 )
374 );
375 }
376
377 private function getQuotaLimitText(): ?string
378 {
379 if (!Loader::includeModule('ai'))
380 {
381 return null;
382 }
383
384 $requestCount = $this->getRequestQuotasSum();
385 if ($requestCount <= 0)
386 {
387 return null;
388 }
389
390 return $this->getRequestLimiter()->getTextFromCheckLimit($requestCount);
391 }
392
398 private function getRequestQuotas(): array
399 {
403 $quotas = [];
404 foreach ($this->scenario->getMap() as $step)
405 {
406 $stepQuota = $step::getRequestQuota($this->generation->getSiteData());
407 if (!$stepQuota)
408 {
409 continue;
410 }
411
412 if (isset($quotas[$stepQuota->connectorClass]))
413 {
414 $quotas[$stepQuota->connectorClass]->requestCount += $stepQuota->requestCount;
415 }
416 else
417 {
418 $quotas[$stepQuota->connectorClass] = $stepQuota;
419 }
420 }
421
422 return $quotas;
423 }
424
429 private function getRequestQuotasSum(): int
430 {
431 $requestCount = 0;
432 foreach ($this->getRequestQuotas() as $quota)
433 {
434 $requestCount += $quota->requestCount;
435 }
436
437 return $requestCount;
438 }
439
445 private function getRequestLimiter(): RequestLimiter
446 {
447 if (empty($this->requestLimiter))
448 {
449 $this->requestLimiter = new RequestLimiter();
450 }
451
452 return $this->requestLimiter;
453 }
454}
onStepChange(callable $callback)
Определения Scenarist.php:165
__construct(IScenario $scenario, Generation $generation)
Определения Scenarist.php:50
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$query
Определения get_search.php:11
$status
Определения session.php:10