Cancel Training
linter / quality (push) Failing after 1m11s
tests / ci (8.3) (push) Successful in 3m55s

This commit is contained in:
2026-09-18 23:08:19 +02:00
parent d80bfe3cdd
commit 25b03fc39a
11 changed files with 168 additions and 33 deletions
@@ -0,0 +1,9 @@
<?php
namespace App\Exceptions;
use RuntimeException;
class TrainingCancelledException extends RuntimeException
{
}
+29 -5
View File
@@ -3,6 +3,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Events\PerceptronInitialization; use App\Events\PerceptronInitialization;
use App\Exceptions\TrainingCancelledException;
use App\Http\Requests\RunPerceptronRequest; use App\Http\Requests\RunPerceptronRequest;
use App\Models\NetworksTraining\ADALINEPerceptronTraining; use App\Models\NetworksTraining\ADALINEPerceptronTraining;
use App\Models\NetworksTraining\GradientDescentPerceptronTraining; use App\Models\NetworksTraining\GradientDescentPerceptronTraining;
@@ -18,9 +19,25 @@ use App\Services\SynapticWeightsProvider\ISynapticWeightsProvider;
use App\Services\SynapticWeightsProvider\RandomSynapticWeights; use App\Services\SynapticWeightsProvider\RandomSynapticWeights;
use App\Services\SynapticWeightsProvider\ZeroSynapticWeights; use App\Services\SynapticWeightsProvider\ZeroSynapticWeights;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class PerceptronController extends Controller class PerceptronController extends Controller
{ {
private function cancellationKey(string $trainingId): string
{
return "perceptron-training-cancelled:{$trainingId}";
}
public function cancel(Request $request)
{
$trainingId = $request->validate([
'training_id' => ['required', 'string', 'max:100'],
])['training_id'];
Cache::put($this->cancellationKey($trainingId), true, now()->addHour());
return response()->noContent();
}
/** /**
* Display the specified resource. * Display the specified resource.
*/ */
@@ -220,6 +237,8 @@ class PerceptronController extends Controller
$sessionId = $request->input('session_id', session()->getId()); $sessionId = $request->input('session_id', session()->getId());
$trainingId = $request->input('training_id'); $trainingId = $request->input('training_id');
Cache::forget($this->cancellationKey($trainingId));
// Zero initialization prevents hidden layers from receiving a gradient. // Zero initialization prevents hidden layers from receiving a gradient.
if ($perceptronType === 'multilayer' && $weightInitMethod === 'zeros') { if ($perceptronType === 'multilayer' && $weightInitMethod === 'zeros') {
$synapticWeightsProvider = new RandomSynapticWeights; $synapticWeightsProvider = new RandomSynapticWeights;
@@ -236,17 +255,22 @@ class PerceptronController extends Controller
$datasetReader = $this->getDataSetReader($dataSet); $datasetReader = $this->getDataSetReader($dataSet);
$networkTraining = match ($perceptronType) { $networkTraining = match ($perceptronType) {
'simple' => new SimpleBinaryPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId), 'simple' => new SimpleBinaryPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId, fn (): bool => connection_aborted() || Cache::has($this->cancellationKey($trainingId))),
'gradientdescent' => new GradientDescentPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId, $minError), 'gradientdescent' => new GradientDescentPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId, $minError, fn (): bool => connection_aborted() || Cache::has($this->cancellationKey($trainingId))),
'adaline' => new ADALINEPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId, $minError), 'adaline' => new ADALINEPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId, $minError, fn (): bool => connection_aborted() || Cache::has($this->cancellationKey($trainingId))),
'monolayer' => new MonoLayerPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId, $minError), 'monolayer' => new MonoLayerPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId, $minError, fn (): bool => connection_aborted() || Cache::has($this->cancellationKey($trainingId))),
'multilayer' => new MultiLayerPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $hiddenLayers, $hiddenLayersNeurons, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId, $minError), 'multilayer' => new MultiLayerPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $hiddenLayers, $hiddenLayersNeurons, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId, $minError, fn (): bool => connection_aborted() || Cache::has($this->cancellationKey($trainingId))),
default => null, default => null,
}; };
event(new PerceptronInitialization($datasetReader->lines, $networkTraining->activationFunction, $sessionId, $trainingId)); event(new PerceptronInitialization($datasetReader->lines, $networkTraining->activationFunction, $sessionId, $trainingId));
try {
$networkTraining->start(); $networkTraining->start();
} catch (TrainingCancelledException) {
$networkTraining->cancel();
Cache::forget($this->cancellationKey($trainingId));
}
return back()->with('success', [ return back()->with('success', [
'message' => 'Training completed', 'message' => 'Training completed',
@@ -8,6 +8,7 @@ use App\Models\Perceptrons\Perceptron;
use App\Services\DatasetReader\IDataSetReader; use App\Services\DatasetReader\IDataSetReader;
use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer; use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer;
use App\Services\SynapticWeightsProvider\ISynapticWeightsProvider; use App\Services\SynapticWeightsProvider\ISynapticWeightsProvider;
use Closure;
class ADALINEPerceptronTraining extends NetworkTraining class ADALINEPerceptronTraining extends NetworkTraining
{ {
@@ -26,8 +27,9 @@ class ADALINEPerceptronTraining extends NetworkTraining
string $sessionId, string $sessionId,
string $trainingId, string $trainingId,
private float $minError, private float $minError,
?Closure $isCancelled = null,
) { ) {
parent::__construct($datasetReader, $maxEpochs, $iterationEventBuffer, $sessionId, $trainingId); parent::__construct($datasetReader, $maxEpochs, $iterationEventBuffer, $sessionId, $trainingId, $isCancelled);
$this->perceptron = new GradientDescentPerceptron($synapticWeightsProvider->generate($datasetReader->getInputSize())); $this->perceptron = new GradientDescentPerceptron($synapticWeightsProvider->generate($datasetReader->getInputSize()));
} }
@@ -8,6 +8,7 @@ use App\Models\Perceptrons\Perceptron;
use App\Services\DatasetReader\IDataSetReader; use App\Services\DatasetReader\IDataSetReader;
use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer; use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer;
use App\Services\SynapticWeightsProvider\ISynapticWeightsProvider; use App\Services\SynapticWeightsProvider\ISynapticWeightsProvider;
use Closure;
class GradientDescentPerceptronTraining extends NetworkTraining class GradientDescentPerceptronTraining extends NetworkTraining
{ {
@@ -26,8 +27,9 @@ class GradientDescentPerceptronTraining extends NetworkTraining
string $sessionId, string $sessionId,
string $trainingId, string $trainingId,
private float $minError, private float $minError,
?Closure $isCancelled = null,
) { ) {
parent::__construct($datasetReader, $maxEpochs, $iterationEventBuffer, $sessionId, $trainingId); parent::__construct($datasetReader, $maxEpochs, $iterationEventBuffer, $sessionId, $trainingId, $isCancelled);
$this->perceptron = new GradientDescentPerceptron($synapticWeightsProvider->generate($datasetReader->getInputSize())); $this->perceptron = new GradientDescentPerceptron($synapticWeightsProvider->generate($datasetReader->getInputSize()));
} }
@@ -10,6 +10,7 @@ use App\Services\DatasetReader\IDataSetReader;
use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer; use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer;
use App\Services\SynapticWeightsProvider\ISynapticWeightsProvider; use App\Services\SynapticWeightsProvider\ISynapticWeightsProvider;
use App\Services\SynapticWeightsProvider\SimpleNetworkWeightsProvider; use App\Services\SynapticWeightsProvider\SimpleNetworkWeightsProvider;
use Closure;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
class MonoLayerPerceptronTraining extends NetworkTraining class MonoLayerPerceptronTraining extends NetworkTraining
@@ -35,8 +36,9 @@ class MonoLayerPerceptronTraining extends NetworkTraining
string $sessionId, string $sessionId,
string $trainingId, string $trainingId,
private float $minError, private float $minError,
?Closure $isCancelled = null,
) { ) {
parent::__construct($datasetReader, $maxEpochs, $iterationEventBuffer, $sessionId, $trainingId); parent::__construct($datasetReader, $maxEpochs, $iterationEventBuffer, $sessionId, $trainingId, $isCancelled);
$this->isRegression = $datasetReader->getInputSize() === 1; $this->isRegression = $datasetReader->getInputSize() === 1;
$networkWeightsProvider = new SimpleNetworkWeightsProvider($synapticWeightsProvider); $networkWeightsProvider = new SimpleNetworkWeightsProvider($synapticWeightsProvider);
$this->network = new NetworkPerceptron( $this->network = new NetworkPerceptron(
@@ -11,6 +11,7 @@ use App\Services\DatasetReader\IDataSetReader;
use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer; use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer;
use App\Services\SynapticWeightsProvider\ISynapticWeightsProvider; use App\Services\SynapticWeightsProvider\ISynapticWeightsProvider;
use App\Services\SynapticWeightsProvider\SimpleNetworkWeightsProvider; use App\Services\SynapticWeightsProvider\SimpleNetworkWeightsProvider;
use Closure;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
class MultiLayerPerceptronTraining extends NetworkTraining class MultiLayerPerceptronTraining extends NetworkTraining
@@ -37,8 +38,9 @@ class MultiLayerPerceptronTraining extends NetworkTraining
string $sessionId, string $sessionId,
string $trainingId, string $trainingId,
private float $minError, private float $minError,
?Closure $isCancelled = null,
) { ) {
parent::__construct($datasetReader, $maxEpochs, $iterationEventBuffer, $sessionId, $trainingId); parent::__construct($datasetReader, $maxEpochs, $iterationEventBuffer, $sessionId, $trainingId, $isCancelled);
$this->labels = $datasetReader->getLabels(); $this->labels = $datasetReader->getLabels();
$this->isRegression = $datasetReader->getOutputSize() === 1 $this->isRegression = $datasetReader->getOutputSize() === 1
|| ($datasetReader->getOutputSize() > 2 || ($datasetReader->getOutputSize() > 2
@@ -3,9 +3,11 @@
namespace App\Models\NetworksTraining; namespace App\Models\NetworksTraining;
use App\Events\PerceptronTrainingEnded; use App\Events\PerceptronTrainingEnded;
use App\Exceptions\TrainingCancelledException;
use App\Models\ActivationsFunctions; use App\Models\ActivationsFunctions;
use App\Services\DatasetReader\IDataSetReader; use App\Services\DatasetReader\IDataSetReader;
use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer; use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer;
use Closure;
abstract class NetworkTraining abstract class NetworkTraining
{ {
@@ -24,6 +26,7 @@ abstract class NetworkTraining
protected IPerceptronIterationEventBuffer $iterationEventBuffer, protected IPerceptronIterationEventBuffer $iterationEventBuffer,
protected string $sessionId, protected string $sessionId,
protected string $trainingId, protected string $trainingId,
protected ?Closure $isCancelled = null,
) {} ) {}
abstract public function start(): void; abstract public function start(): void;
@@ -50,9 +53,18 @@ abstract class NetworkTraining
protected function addIterationToBuffer(float $error, array $synapticWeights) protected function addIterationToBuffer(float $error, array $synapticWeights)
{ {
if ($this->isCancelled !== null && ($this->isCancelled)()) {
throw new TrainingCancelledException;
}
$this->iterationEventBuffer->addIteration($this->epoch, $this->datasetReader->getLastReadLineIndex(), $error, $synapticWeights); $this->iterationEventBuffer->addIteration($this->epoch, $this->datasetReader->getLastReadLineIndex(), $error, $synapticWeights);
} }
public function cancel(): void
{
$this->broadcastTrainingEnded('Entraînement annulé');
}
public function getEpoch(): int public function getEpoch(): int
{ {
return $this->epoch; return $this->epoch;
@@ -9,6 +9,7 @@ use App\Models\Perceptrons\SimpleBinaryPerceptron;
use App\Services\DatasetReader\IDataSetReader; use App\Services\DatasetReader\IDataSetReader;
use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer; use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer;
use App\Services\SynapticWeightsProvider\ISynapticWeightsProvider; use App\Services\SynapticWeightsProvider\ISynapticWeightsProvider;
use Closure;
class SimpleBinaryPerceptronTraining extends NetworkTraining class SimpleBinaryPerceptronTraining extends NetworkTraining
{ {
@@ -28,8 +29,9 @@ class SimpleBinaryPerceptronTraining extends NetworkTraining
IPerceptronIterationEventBuffer $iterationEventBuffer, IPerceptronIterationEventBuffer $iterationEventBuffer,
string $sessionId, string $sessionId,
string $trainingId, string $trainingId,
?Closure $isCancelled = null,
) { ) {
parent::__construct($datasetReader, $maxEpochs, $iterationEventBuffer, $sessionId, $trainingId); parent::__construct($datasetReader, $maxEpochs, $iterationEventBuffer, $sessionId, $trainingId, $isCancelled);
$this->perceptron = new SimpleBinaryPerceptron($synapticWeightsProvider->generate($datasetReader->getInputSize())); $this->perceptron = new SimpleBinaryPerceptron($synapticWeightsProvider->generate($datasetReader->getInputSize()));
} }
+95 -21
View File
@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { useForm } from '@inertiajs/vue3'; import { router, useForm } from '@inertiajs/vue3';
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import { import {
Form, Form,
@@ -12,6 +12,7 @@ import {
NativeSelect, NativeSelect,
NativeSelectOption, NativeSelectOption,
} from '@/components/ui/native-select'; } from '@/components/ui/native-select';
import { cancel } from '@/routes/perceptron';
import type { import type {
Dataset, Dataset,
InitializationMethod, InitializationMethod,
@@ -23,8 +24,9 @@ import Card from './ui/card/Card.vue';
import CardContent from './ui/card/CardContent.vue'; import CardContent from './ui/card/CardContent.vue';
import CardHeader from './ui/card/CardHeader.vue'; import CardHeader from './ui/card/CardHeader.vue';
import CardTitle from './ui/card/CardTitle.vue'; import CardTitle from './ui/card/CardTitle.vue';
import Input from './ui/input/Input.vue';
import FormError from './ui/form/FormError.vue'; import FormError from './ui/form/FormError.vue';
import Input from './ui/input/Input.vue';
import Spinner from './ui/spinner/Spinner.vue';
const props = defineProps<{ const props = defineProps<{
type: PerceptronType; type: PerceptronType;
@@ -98,35 +100,50 @@ watch(maxIterations, (value) => {
watch(selectedDatasetCopy, (newvalue) => { watch(selectedDatasetCopy, (newvalue) => {
form.clearErrors('dataset'); form.clearErrors('dataset');
const selectedDatasetCopy = props.datasets.find( const selectedDatasetCopy =
(dataset) => dataset.label === newvalue props.datasets.find((dataset) => dataset.label === newvalue) || null;
) || null;
// LearningRate // LearningRate
learningRate.value = props.defaultLearningRate; learningRate.value = props.defaultLearningRate;
if (selectedDatasetCopy && selectedDatasetCopy.defaultLearningRate !== undefined) { if (
selectedDatasetCopy &&
selectedDatasetCopy.defaultLearningRate !== undefined
) {
learningRate.value = selectedDatasetCopy.defaultLearningRate; learningRate.value = selectedDatasetCopy.defaultLearningRate;
} }
// MinError // MinError
minError.value = props.minError; minError.value = props.minError;
if (selectedDatasetCopy && selectedDatasetCopy.defaultMinError !== undefined) { if (
selectedDatasetCopy &&
selectedDatasetCopy.defaultMinError !== undefined
) {
minError.value = selectedDatasetCopy.defaultMinError; minError.value = selectedDatasetCopy.defaultMinError;
} }
// MaxIterations // MaxIterations
maxIterations.value = props.defaultMaxIterations; maxIterations.value = props.defaultMaxIterations;
if (selectedDatasetCopy && selectedDatasetCopy.defaultMaxIterations !== undefined) { if (
selectedDatasetCopy &&
selectedDatasetCopy.defaultMaxIterations !== undefined
) {
maxIterations.value = selectedDatasetCopy.defaultMaxIterations; maxIterations.value = selectedDatasetCopy.defaultMaxIterations;
} }
// HiddenLayers // HiddenLayers
hiddenLayers.value = props.hiddenLayers; hiddenLayers.value = props.hiddenLayers;
if (selectedDatasetCopy && selectedDatasetCopy.defaultHiddenLayers !== undefined) { if (
selectedDatasetCopy &&
selectedDatasetCopy.defaultHiddenLayers !== undefined
) {
hiddenLayers.value = selectedDatasetCopy.defaultHiddenLayers; hiddenLayers.value = selectedDatasetCopy.defaultHiddenLayers;
} }
hiddenLayersNeurons.value = props.hiddenLayersNeurons; hiddenLayersNeurons.value = props.hiddenLayersNeurons;
if (selectedDatasetCopy && selectedDatasetCopy.defaultHiddenLayersNeurons !== undefined) { if (
hiddenLayersNeurons.value = selectedDatasetCopy.defaultHiddenLayersNeurons; selectedDatasetCopy &&
selectedDatasetCopy.defaultHiddenLayersNeurons !== undefined
) {
hiddenLayersNeurons.value =
selectedDatasetCopy.defaultHiddenLayersNeurons;
} }
}) });
const trainingId = ref<string>(''); const trainingId = ref<string>('');
@@ -134,7 +151,7 @@ function startTraining() {
if (!selectedDatasetCopy.value) { if (!selectedDatasetCopy.value) {
form.setError( form.setError(
'dataset', 'dataset',
'Un dataset est nécessaire avant de lancer l\'entraînement.', "Un dataset est nécessaire avant de lancer l'entraînement.",
); );
console.log(form.errors); console.log(form.errors);
return; return;
@@ -169,6 +186,21 @@ function startTraining() {
} }
const emit = defineEmits(['update:selectedDataset', 'update:trainingId']); const emit = defineEmits(['update:selectedDataset', 'update:trainingId']);
async function cancelTraining() {
form.cancel();
router.post(
cancel(),
{
training_id: trainingId.value,
},
{
preserveScroll: true,
},
);
}
watch(selectedDatasetCopy, (newValue) => { watch(selectedDatasetCopy, (newValue) => {
emit('update:selectedDataset', newValue); emit('update:selectedDataset', newValue);
}); });
@@ -182,6 +214,7 @@ watch(selectedDatasetCopy, (newValue) => {
<CardContent> <CardContent>
<Form <Form
class="grid auto-cols-max grid-flow-row grid-cols-1 gap-4 space-y-6 md:grid-cols-2" class="grid auto-cols-max grid-flow-row grid-cols-1 gap-4 space-y-6 md:grid-cols-2"
cancel-on-unmount
> >
<!-- DATASET --> <!-- DATASET -->
<FormField name="dataset"> <FormField name="dataset">
@@ -206,7 +239,12 @@ watch(selectedDatasetCopy, (newValue) => {
</NativeSelectOption> </NativeSelectOption>
</NativeSelect> </NativeSelect>
</FormControl> </FormControl>
<FormError :error="form.errors.dataset || props.errors?.selectedDatasetCopy" /> <FormError
:error="
form.errors.dataset ||
props.errors?.selectedDatasetCopy
"
/>
</FormItem> </FormItem>
</FormField> </FormField>
@@ -224,7 +262,9 @@ watch(selectedDatasetCopy, (newValue) => {
class="cursor-pointer" class="cursor-pointer"
> >
<NativeSelectOption <NativeSelectOption
v-for="method in (props.type == 'multilayer' ? ['random'] : ['zeros', 'random'])" v-for="method in props.type == 'multilayer'
? ['random']
: ['zeros', 'random']"
v-bind:key="method" v-bind:key="method"
:value="method" :value="method"
> >
@@ -236,7 +276,10 @@ watch(selectedDatasetCopy, (newValue) => {
</FormField> </FormField>
<!-- HIDDEN LAYERS --> <!-- HIDDEN LAYERS -->
<FormField name="hidden_layers" v-if="props.type === 'multilayer'"> <FormField
name="hidden_layers"
v-if="props.type === 'multilayer'"
>
<FormItem> <FormItem>
<FormLabel>Nombre de couches cachées</FormLabel> <FormLabel>Nombre de couches cachées</FormLabel>
<FormControl> <FormControl>
@@ -255,9 +298,14 @@ watch(selectedDatasetCopy, (newValue) => {
</FormField> </FormField>
<!-- HIDDEN LAYERS NEURONS --> <!-- HIDDEN LAYERS NEURONS -->
<FormField name="hidden_layers_neurons" v-if="props.type === 'multilayer'"> <FormField
name="hidden_layers_neurons"
v-if="props.type === 'multilayer'"
>
<FormItem> <FormItem>
<FormLabel>Nombre de neurones par couche cachée</FormLabel> <FormLabel
>Nombre de neurones par couche cachée</FormLabel
>
<FormControl> <FormControl>
<!-- TODO : MAX input --> <!-- TODO : MAX input -->
<Input <Input
@@ -319,13 +367,39 @@ watch(selectedDatasetCopy, (newValue) => {
@input="handleMaxIterationsInput" @input="handleMaxIterationsInput"
/> />
</FormControl> </FormControl>
<div v-if="form.errors.max_iterations || props.errors.max_iterations"> <div
{{ form.errors.max_iterations || props.errors.max_iterations }} v-if="
form.errors.max_iterations ||
props.errors.max_iterations
"
>
{{
form.errors.max_iterations ||
props.errors.max_iterations
}}
</div> </div>
</FormItem> </FormItem>
</FormField> </FormField>
</Form> </Form>
<Button variant="outline" class="cursor-pointer mt-6" @click="startTraining">Lancer</Button>
<Transition name="fade">
<Button
variant="outline"
class="mt-6 cursor-pointer"
:disabled="form.processing"
@click="startTraining"
>Lancer<Spinner v-if="form.processing" class="ml-1"
/></Button>
</Transition>
<Transition name="fade">
<Button
variant="outline"
class="mt-6 ml-4 cursor-pointer"
@click="cancelTraining"
v-if="form.processing"
>Annuler</Button
>
</Transition>
</CardContent> </CardContent>
</Card> </Card>
</template> </template>
+1
View File
@@ -15,6 +15,7 @@ import {
LineController, LineController,
BarController, BarController,
LogarithmicScale, LogarithmicScale,
} from 'chart.js'; } from 'chart.js';
import { ArrowDown, ArrowUp } from 'lucide-vue-next'; import { ArrowDown, ArrowUp } from 'lucide-vue-next';
import { computed, nextTick, ref, watch } from 'vue'; import { computed, nextTick, ref, watch } from 'vue';
+5
View File
@@ -3,6 +3,7 @@
use App\Http\Controllers\PerceptronController; use App\Http\Controllers\PerceptronController;
use Illuminate\Support\Facades\Broadcast; use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Illuminate\Session\Middleware\StartSession;
use Inertia\Inertia; use Inertia\Inertia;
// Route::redirect('/', '/perceptron?type=simple')->name('home'); // Route::redirect('/', '/perceptron?type=simple')->name('home');
@@ -15,6 +16,10 @@ Route::middleware('throttle:training')->post(
[PerceptronController::class, 'run'], [PerceptronController::class, 'run'],
)->name('perceptron.run'); )->name('perceptron.run');
Route::post('perceptron/cancel', [PerceptronController::class, 'cancel'])
->name('perceptron.cancel')
->withoutMiddleware(StartSession::class);
Route::resource('perceptron', PerceptronController::class)->only(['index']); Route::resource('perceptron', PerceptronController::class)->only(['index']);
Broadcast::routes(); Broadcast::routes();