options->get(self::OPTION, null); if ($raw === null || $raw === false || $raw === '') { return EmbeddingConfig::lexical($siteId); } if (!is_array($raw) || ($raw['v'] ?? null) !== EmbeddingConfig::SCHEMA || (int) ($raw['site_id'] ?? 0) !== $siteId) { return EmbeddingConfig::lexical($siteId, 0, true); } $version = is_int($raw['version'] ?? null) ? $raw['version'] : 0; if (($raw['mode'] ?? null) === EmbeddingConfig::MODE_LEXICAL) { return EmbeddingConfig::lexical($siteId, $version); } try { return EmbeddingConfig::semantic( $siteId, is_string($raw['provider'] ?? null) ? $raw['provider'] : '', is_string($raw['model'] ?? null) ? $raw['model'] : '', is_int($raw['dimension'] ?? null) ? $raw['dimension'] : 0, is_int($raw['connection_id'] ?? null) ? $raw['connection_id'] : null, $version ); } catch (InvalidEmbeddingConfig) { return EmbeddingConfig::lexical($siteId, $version, true); } } /** * @param (callable(int, int): bool)|null $connectionBelongsToSite checks that a referenced connection exists on this site * @throws InvalidEmbeddingConfig */ public function save(EmbeddingConfig $config, ?callable $connectionBelongsToSite = null): EmbeddingConfig { if ($config->connectionId !== null && ($connectionBelongsToSite === null || !$connectionBelongsToSite($config->siteId, $config->connectionId))) { throw new InvalidEmbeddingConfig('connection_not_found'); } $next = $this->get($config->siteId)->version + 1; $saved = $config->isSemantic() ? EmbeddingConfig::semantic($config->siteId, (string) $config->provider, (string) $config->model, (int) $config->dimension, $config->connectionId, $next) : EmbeddingConfig::lexical($config->siteId, $next); $this->options->set(self::OPTION, $saved->toArray()); return $saved; } }