mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-05-18 01:11:32 +00:00
Allow to add selectors to ai model selects and only show models supporting structured output for AI extractor
This commit is contained in:
parent
9d389309fc
commit
368dd14785
3 changed files with 47 additions and 22 deletions
|
|
@ -22,37 +22,37 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
use App\Entity\Parameters\AbstractParameter;
|
|
||||||
use App\Services\AI\AIPlatformRegistry;
|
|
||||||
use App\Services\AI\AIPlatforms;
|
|
||||||
use App\Settings\MiscSettings\IpnSuggestSettings;
|
|
||||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use App\Entity\Attachments\Attachment;
|
use App\Entity\Attachments\Attachment;
|
||||||
use App\Entity\Parts\Category;
|
use App\Entity\Parameters\AbstractParameter;
|
||||||
use App\Entity\Parts\Footprint;
|
|
||||||
use App\Entity\Parameters\AttachmentTypeParameter;
|
use App\Entity\Parameters\AttachmentTypeParameter;
|
||||||
use App\Entity\Parameters\CategoryParameter;
|
use App\Entity\Parameters\CategoryParameter;
|
||||||
use App\Entity\Parameters\ProjectParameter;
|
|
||||||
use App\Entity\Parameters\FootprintParameter;
|
use App\Entity\Parameters\FootprintParameter;
|
||||||
use App\Entity\Parameters\GroupParameter;
|
use App\Entity\Parameters\GroupParameter;
|
||||||
use App\Entity\Parameters\ManufacturerParameter;
|
use App\Entity\Parameters\ManufacturerParameter;
|
||||||
use App\Entity\Parameters\MeasurementUnitParameter;
|
use App\Entity\Parameters\MeasurementUnitParameter;
|
||||||
use App\Entity\Parameters\PartParameter;
|
use App\Entity\Parameters\PartParameter;
|
||||||
|
use App\Entity\Parameters\ProjectParameter;
|
||||||
use App\Entity\Parameters\StorageLocationParameter;
|
use App\Entity\Parameters\StorageLocationParameter;
|
||||||
use App\Entity\Parameters\SupplierParameter;
|
use App\Entity\Parameters\SupplierParameter;
|
||||||
|
use App\Entity\Parts\Category;
|
||||||
|
use App\Entity\Parts\Footprint;
|
||||||
use App\Entity\Parts\Part;
|
use App\Entity\Parts\Part;
|
||||||
use App\Entity\PriceInformations\Currency;
|
use App\Entity\PriceInformations\Currency;
|
||||||
use App\Repository\ParameterRepository;
|
use App\Repository\ParameterRepository;
|
||||||
|
use App\Services\AI\AIPlatformRegistry;
|
||||||
|
use App\Services\AI\AIPlatforms;
|
||||||
use App\Services\Attachments\AttachmentURLGenerator;
|
use App\Services\Attachments\AttachmentURLGenerator;
|
||||||
use App\Services\Attachments\BuiltinAttachmentsFinder;
|
use App\Services\Attachments\BuiltinAttachmentsFinder;
|
||||||
use App\Services\Attachments\PartPreviewGenerator;
|
use App\Services\Attachments\PartPreviewGenerator;
|
||||||
use App\Services\Tools\TagFinder;
|
use App\Services\Tools\TagFinder;
|
||||||
|
use App\Settings\MiscSettings\IpnSuggestSettings;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\AI\Platform\Capability;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\Asset\Packages;
|
use Symfony\Component\Asset\Packages;
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
||||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||||
|
|
@ -126,9 +126,12 @@ class TypeaheadController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route(path: '/parts/search/{query}', name: 'typeahead_parts')]
|
#[Route(path: '/parts/search/{query}', name: 'typeahead_parts')]
|
||||||
public function parts(EntityManagerInterface $entityManager, PartPreviewGenerator $previewGenerator,
|
public function parts(
|
||||||
AttachmentURLGenerator $attachmentURLGenerator, string $query = ""): JsonResponse
|
EntityManagerInterface $entityManager,
|
||||||
{
|
PartPreviewGenerator $previewGenerator,
|
||||||
|
AttachmentURLGenerator $attachmentURLGenerator,
|
||||||
|
string $query = ""
|
||||||
|
): JsonResponse {
|
||||||
$this->denyAccessUnlessGranted('@parts.read');
|
$this->denyAccessUnlessGranted('@parts.read');
|
||||||
|
|
||||||
$repo = $entityManager->getRepository(Part::class);
|
$repo = $entityManager->getRepository(Part::class);
|
||||||
|
|
@ -139,7 +142,7 @@ class TypeaheadController extends AbstractController
|
||||||
foreach ($parts as $part) {
|
foreach ($parts as $part) {
|
||||||
//Determine the picture to show:
|
//Determine the picture to show:
|
||||||
$preview_attachment = $previewGenerator->getTablePreviewAttachment($part);
|
$preview_attachment = $previewGenerator->getTablePreviewAttachment($part);
|
||||||
if($preview_attachment instanceof Attachment) {
|
if ($preview_attachment instanceof Attachment) {
|
||||||
$preview_url = $attachmentURLGenerator->getThumbnailURL($preview_attachment, 'thumbnail_sm');
|
$preview_url = $attachmentURLGenerator->getThumbnailURL($preview_attachment, 'thumbnail_sm');
|
||||||
} else {
|
} else {
|
||||||
$preview_url = '';
|
$preview_url = '';
|
||||||
|
|
@ -153,7 +156,7 @@ class TypeaheadController extends AbstractController
|
||||||
'footprint' => $part->getFootprint() instanceof Footprint ? $part->getFootprint()->getName() : '',
|
'footprint' => $part->getFootprint() instanceof Footprint ? $part->getFootprint()->getName() : '',
|
||||||
'description' => mb_strimwidth($part->getDescription(), 0, 127, '...'),
|
'description' => mb_strimwidth($part->getDescription(), 0, 127, '...'),
|
||||||
'image' => $preview_url,
|
'image' => $preview_url,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return new JsonResponse($data);
|
return new JsonResponse($data);
|
||||||
|
|
@ -224,7 +227,8 @@ class TypeaheadController extends AbstractController
|
||||||
|
|
||||||
|
|
||||||
$partRepository = $entityManager->getRepository(Part::class);
|
$partRepository = $entityManager->getRepository(Part::class);
|
||||||
$ipnSuggestions = $partRepository->autoCompleteIpn($clonedPart, $description, $this->ipnSuggestSettings->suggestPartDigits);
|
$ipnSuggestions = $partRepository->autoCompleteIpn($clonedPart, $description,
|
||||||
|
$this->ipnSuggestSettings->suggestPartDigits);
|
||||||
|
|
||||||
return new JsonResponse($ipnSuggestions);
|
return new JsonResponse($ipnSuggestions);
|
||||||
}
|
}
|
||||||
|
|
@ -232,16 +236,26 @@ class TypeaheadController extends AbstractController
|
||||||
#[Route(path: '/ai/{platform}/models', name: 'typeahead_ai_models', requirements: ['platform' => '.+'])]
|
#[Route(path: '/ai/{platform}/models', name: 'typeahead_ai_models', requirements: ['platform' => '.+'])]
|
||||||
public function aiModels(
|
public function aiModels(
|
||||||
AIPlatforms $platform,
|
AIPlatforms $platform,
|
||||||
|
Request $request,
|
||||||
AIPlatformRegistry $platformRegistry,
|
AIPlatformRegistry $platformRegistry,
|
||||||
CacheInterface $cache,
|
CacheInterface $cache,
|
||||||
): JsonResponse {
|
): JsonResponse {
|
||||||
|
|
||||||
$this->denyAccessUnlessGranted('@config.change_system_settings');
|
$this->denyAccessUnlessGranted('@config.change_system_settings');
|
||||||
|
|
||||||
$models = $cache->get('ai_models_'.$platform->value, function(ItemInterface $item) use ($platformRegistry, $platform) {
|
$capability_filter = $request->query->getEnum('capability', Capability::class);
|
||||||
$item->expiresAfter(3600); //Cache for 1 hour
|
|
||||||
return $platformRegistry->getPlatform($platform)->getModelCatalog()->getModels();
|
$models = $cache->get('ai_models_'.$platform->value.'_'.($capability_filter?->value ?? 'all'),
|
||||||
});
|
function (ItemInterface $item) use ($platformRegistry, $platform, $capability_filter) {
|
||||||
|
$item->expiresAfter(3600); //Cache for 1 hour
|
||||||
|
if ($capability_filter === null) {
|
||||||
|
return $platformRegistry->getPlatform($platform)->getModelCatalog()->getModels();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Otherwise filter the models by the capability
|
||||||
|
return array_filter($platformRegistry->getPlatform($platform)->getModelCatalog()->getModels(),
|
||||||
|
static fn(array $model) => in_array($capability_filter, $model['capabilities'] ?? [], true)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return new JsonResponse($models);
|
return new JsonResponse($models);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Form\Settings;
|
namespace App\Form\Settings;
|
||||||
|
|
||||||
|
use Symfony\AI\Platform\Capability;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
|
@ -50,11 +51,20 @@ final class AiModelsType extends AbstractType
|
||||||
//The target label of the platform select, which is used to filter the models for the selected platform.
|
//The target label of the platform select, which is used to filter the models for the selected platform.
|
||||||
$resolver->setRequired('platform_selector');
|
$resolver->setRequired('platform_selector');
|
||||||
$resolver->setAllowedTypes('platform_selector', 'string');
|
$resolver->setAllowedTypes('platform_selector', 'string');
|
||||||
|
|
||||||
|
//Only show models, that have the given capability. This is used to only show models that support structured output for the AI extractor settings.
|
||||||
|
$resolver->setDefault('filter_capability', null);
|
||||||
|
$resolver->setAllowedTypes('filter_capability', ['null', Capability::class]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function finishView(FormView $view, FormInterface $form, array $options): void
|
public function finishView(FormView $view, FormInterface $form, array $options): void
|
||||||
{
|
{
|
||||||
$view->vars['attr']['data-url-template'] = $this->urlGenerator->generate('typeahead_ai_models', ['platform' => '__PLATFORM__']);
|
$urlOptions = ['platform' => '__PLATFORM__'];
|
||||||
|
if ($options['filter_capability'] !== null) {
|
||||||
|
$urlOptions['capability'] = $options['filter_capability']->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
$view->vars['attr']['data-url-template'] = $this->urlGenerator->generate('typeahead_ai_models', $urlOptions);
|
||||||
$view->vars['attr']['data-controller'] = 'elements--ai-model-autocomplete';
|
$view->vars['attr']['data-controller'] = 'elements--ai-model-autocomplete';
|
||||||
|
|
||||||
$view->vars['attr']['data-platform-selector'] = $options['platform_selector'];
|
$view->vars['attr']['data-platform-selector'] = $options['platform_selector'];
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
|
||||||
use Jbtronics\SettingsBundle\Settings\Settings;
|
use Jbtronics\SettingsBundle\Settings\Settings;
|
||||||
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
|
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
|
||||||
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
|
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
|
||||||
|
use Symfony\AI\Platform\Capability;
|
||||||
use Symfony\Component\Translation\TranslatableMessage as TM;
|
use Symfony\Component\Translation\TranslatableMessage as TM;
|
||||||
|
|
||||||
#[Settings(name: "ai_extractor", label: new TM("settings.ips.ai_extractor"), description: new TM("settings.ips.ai_extractor.description"))]
|
#[Settings(name: "ai_extractor", label: new TM("settings.ips.ai_extractor"), description: new TM("settings.ips.ai_extractor.description"))]
|
||||||
|
|
@ -48,7 +49,7 @@ class AIExtractorSettings
|
||||||
public ?AIPlatforms $platform = null;
|
public ?AIPlatforms $platform = null;
|
||||||
|
|
||||||
#[SettingsParameter(label: new TM("settings.ips.ai_extractor.model"), description: new TM("settings.ips.ai_extractor.model.description"),
|
#[SettingsParameter(label: new TM("settings.ips.ai_extractor.model"), description: new TM("settings.ips.ai_extractor.model.description"),
|
||||||
formType: AiModelsType::class, formOptions: ['platform_selector' => self::MODEL_SELECTOR_LABEL],
|
formType: AiModelsType::class, formOptions: ['platform_selector' => self::MODEL_SELECTOR_LABEL, 'filter_capability' => Capability::OUTPUT_STRUCTURED],
|
||||||
envVar: "string:PROVIDER_AI_EXTRACTOR_MODEL", envVarMode: EnvVarMode::OVERWRITE
|
envVar: "string:PROVIDER_AI_EXTRACTOR_MODEL", envVarMode: EnvVarMode::OVERWRITE
|
||||||
)]
|
)]
|
||||||
public string $model = 'z-ai/glm-4.7';
|
public string $model = 'z-ai/glm-4.7';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue