Skip to content

Eigene Anbieter

Jedes Übersetzungs-Backend der Erweiterung implementiert ein Interface und hat ein Service-Tag: DeepL, Google Translate, die vier LLM-Anbieter und modernice All-in-One. Ihr eigener Anbieter nimmt exakt denselben Weg.

Ein getaggter Anbieter erscheint automatisch in der Anbieterliste der Administration, im Assistenten und im Dialog für die Schnellübersetzung, in der Zuordnung je Sprache, in der Kostenschätzung, in der CLI und in GET /api/_action/nice-translate/providers.

TranslationProviderInterface ist der verpflichtende Vertrag. Das zweite Interface, SupportsLiveModelsInterface, ist optional. Es schaltet die Live-Modellliste frei, und Optional: Live-Modellliste beschreibt es.

Das Interface

Nice\Translate\Provider\TranslationProviderInterface:

php
<?php declare(strict_types=1);

namespace Nice\Translate\Provider;

use Nice\Translate\Provider\Dto\Cost;
use Nice\Translate\Provider\Dto\CredentialStatus;
use Nice\Translate\Provider\Dto\ProviderRequest;
use Nice\Translate\Provider\Dto\ProviderResult;
use Nice\Translate\Provider\Exception\ProviderException;

interface TranslationProviderInterface
{
    /**
     * 'deepl'|'google'|'openai'|'anthropic'|'gemini'|'mistral'|'managed'
     */
    public function getId(): string;

    public function getLabel(): string;

    /**
     * API-Schlüssel vorhanden (managed: Abonnement aktiv).
     */
    public function isConfigured(): bool;

    /**
     * @throws ProviderException
     */
    public function translate(ProviderRequest $request): ProviderResult;

    public function validateCredentials(?string $apiKey = null): CredentialStatus;

    /**
     * Kuratierte Modellliste; leer bei maschinellen Übersetzern.
     *
     * `name` ist der reine Produktname (kein Fließtext), `tier` eine grobe
     * Einordnung nach Qualität/Preis, `pricing` gilt pro 1M Tokens
     * (null, wenn unbekannt).
     *
     * @return list<array{
     *     id: string,
     *     name: string,
     *     tier: 'quality'|'balanced'|'budget'|null,
     *     pricing: array{input: float, output: float, currency: string}|null,
     *     source: 'curated'|'live',
     * }>
     */
    public function getModels(): array;

    /**
     * @param 'html'|'formality'|'tone'|'glossary' $feature
     */
    public function supports(string $feature): bool;

    public function estimate(int $characters): Cost;

    /**
     * Maximale Anzahl Texte pro translate()-Aufruf.
     */
    public function getMaxBatchSize(): int;

    /**
     * Maximale Gesamtgröße der Texte in Bytes pro translate()-Aufruf.
     */
    public function getMaxChunkBytes(): int;
}

Verträge der Methoden

getId()

ProviderRegistry indiziert die Anbieter über diese ID. Eine doppelt vergebene ID überschreibt den zuvor registrierten Service stillschweigend, wählen Sie deshalb eine eindeutige ID.

Die Erweiterung speichert die ID in nice_translate_job.provider_id und nice_translate_usage.provider_id, und beide Spalten sind VARCHAR(32). Bleiben Sie bei höchstens 32 Zeichen. Nutzen Sie einen stabilen, kleingeschriebenen Bezeichner: Er wird auch zum Schlüssel in der Zuordnung languageProviderMap und zum Präfix der Konfigurationsschlüssel auf der Einstellungsseite (siehe Registrierung).

getLabel()

Geben Sie den lesbaren Namen für den Assistenten, die Einstellungsseite, die CLI-Tabelle und die erzeugten Auftragstitel zurück. Die Erweiterung übersetzt ihn nicht. Wählen Sie deshalb einen Namen, der zu jeder Sprache passt, so wie die eingebauten Anbieter DeepL oder Google Translate zurückgeben.

isConfigured()

Melden Sie, ob der Anbieter in diesem Moment einsatzbereit ist. Bei den eingebauten Anbietern bedeutet das, ob ein Schlüssel hinterlegt ist. Die Methode steuert das Kennzeichen Konfiguriert, das Laden der Live-Modelle und die Schätzungswarnung provider_not_configured.

Jeder Aufbau der Anbieterliste ruft diese Methode für jeden Anbieter auf. Führen Sie darin keine Netzwerk-I/O aus. Alle eingebauten Anbieter lesen ausschließlich die Systemkonfiguration.

translate()

Dies ist die eine Methode, die echte Arbeit leisten muss. Sie erhält einen ProviderRequest und muss ein ProviderResult zurückgeben, dessen texts dieselbe Länge und dieselbe Reihenfolge wie $request->texts hat. Eine abweichende Länge wertet die Engine als Fehler, auch wenn Sie keine Exception werfen.

Werfen Sie nach einem Fehler eine ProviderException. Bei einem leeren texts-Array liefern Sie ein leeres Ergebnis und fragen nichts upstream an.

Die Werte characters und cost, die Sie melden, sind die maßgebliche Abrechnungsgrundlage des Laufs. Die Engine verbucht sie im Auftrag und in der Monatsnutzung, sobald der Aufruf zurückkehrt. Das gilt auch für eine Antwort, die danach an der Strukturprüfung scheitert.

validateCredentials()

POST /provider/{providerId}/test und der CLI-Befehl nice-translate:providers --test rufen diese Methode auf. Ein ausdrücklich übergebener $apiKey hat Vorrang vor der gespeicherten Konfiguration, deshalb testet die Administration einen eingegebenen, aber nicht gespeicherten Schlüssel.

Folgen Sie den eingebauten Anbietern und geben Sie bei einem Problem mit den Zugangsdaten new CredentialStatus(false, '<user-safe message>') zurück. Der Controller fängt zusätzlich eine ProviderException ab. Schreiben Sie niemals den Schlüssel selbst in die Meldung, denn die Administration zeigt sie an.

getModels()

Geben Sie Ihre kuratierte Modellliste zurück, oder [] für einen reinen maschinellen Übersetzer, so wie DeepL, Google und der Managed-Anbieter es tun. In jedem Eintrag ist name der reine Produktname ohne Fließtext, tier eine grobe Einordnung nach Qualität und Preis, und pricing der Preis pro einer Million Tokens (oder null, wenn Sie ihn nicht kennen). Die Administration setzt die Bezeichnung als Name · Stufe · Preis zusammen.

Für eine Live-Abfrage implementieren Sie zusätzlich SupportsLiveModelsInterface.

supports()

Die Administration fragt genau vier Feature-Bezeichner ab:

FeatureBedeutung
htmlDer Anbieter nimmt HTML-Markup mit format: 'html' an und liefert es unversehrt zurück
formalityDer Anbieter hat eine eigene Steuerung der Formalität (DeepL ist der Referenzfall)
toneDer Anbieter berücksichtigt das Feld tone der Anfrage
glossaryDer Anbieter berücksichtigt glossaryTerms in seiner eigenen Anfrage, statt sich allein auf die Nachbearbeitung zu verlassen

Geben Sie false für jedes Feature zurück, das Sie nicht umsetzen, und für jede Zeichenkette, die Sie nicht kennen. Ein gemeldetes, aber nicht umgesetztes Feature führt den Händler in den Einstellungen in die Irre. Die Engine wendet ihren Platzhalterschutz und ihre Glossar-Nachbearbeitung in beiden Fällen an.

estimate()

Berechnen Sie eine unverbindliche Vorabkalkulation für eine Zeichenzahl. Halten Sie sie günstig, und fragen Sie nichts upstream an: Sie läuft einmal je Schätzungsanfrage, und der Assistent schätzt bei jeder Änderung der Auswahl neu.

JobService::estimate() verschluckt eine geworfene Exception und fällt auf 0.0 USD zurück. Eine kaputte Schätzung verschlechtert deshalb die Zahl und lässt die Anfrage nicht scheitern. Geben Sie die Währung zurück, in der Sie tatsächlich abrechnen. Die Engine addiert keine Kosten unterschiedlicher Währungen innerhalb eines Batches.

getMaxBatchSize() und getMaxChunkBytes()

Siehe Batching und Chunking.

Datentransferobjekte

Alle DTOs liegen in Nice\Translate\Provider\Dto. Es sind final readonly-Klassen mit öffentlichen, im Konstruktor promoteten Eigenschaften.

ProviderRequest

Die Locale-Codes sind Shopware-ISO-Codes wie de-DE. Bilden Sie sie innerhalb Ihres Anbieters auf die Codes Ihres Upstreams ab. Die eingebauten Anbieter nutzen dafür LocaleMapper.

EigenschaftTypStandardBedeutung
textslist<string>Die zu übersetzenden Texte, in erhaltener Reihenfolge
sourceLocalestringShopware-ISO-Code der Ausgangssprache
targetLocalestringShopware-ISO-Code der Zielsprache
format'html'|'text''text'HTML und reiner Text werden immer in getrennten Anfragen gesendet
tone?stringnullformal oder informal, oder null
customPrompt?stringnullDie eigenen Anweisungen des Händlers
glossaryTermsarray<string, string|null>[]Begriff ⇒ feste Übersetzung. null bedeutet „diesen Begriff nie übersetzen“
serviceTier?stringnullNur beim Managed-Weg gesetzt
idempotencyKey?stringnullStabil je logischem Anbieteraufruf, auch bei Bisektionsaufrufen
contentType?stringnullDer Entitätsname, aus dem die Texte stammen
traceId?stringnullDie Request-ID des Batches oder die Auftrags-ID

ProviderResult

EigenschaftTypStandardBedeutung
textslist<string>Übersetzte Texte, gleiche Länge und Reihenfolge wie in der Anfrage
charactersintZeichen, die Sie als abrechenbar ansehen
inputTokensint0Die Engine erfasst sie in der Monatsnutzung
outputTokensint0Die Engine erfasst sie in der Monatsnutzung
costCostnew Cost(0.0, 'USD')Die Engine summiert ihn im Auftrag und in der Monatsnutzung auf
managedUsage?arraynullNur für die Managed-Abrechnung
managedQuota?arraynullNur für die Managed-Abrechnung
accountingKey?stringnullStabiler Idempotenzschlüssel der Upstream-Belastung

accountingKey erfasst die Nutzung auch bei erneuter Messenger-Zustellung genau einmal. Bei einer gültigen UUID trägt UsageRecorder ihn zuerst in eine Dedupe-Tabelle ein und überspringt die Monatsaktualisierung, wenn diese Zeile bereits existiert. Lassen Sie ihn auf null, solange Ihr Upstream Ihnen keine stabile ID je Belastung liefert.

Cost

php
final readonly class Cost implements \JsonSerializable
{
    public function __construct(
        public float $amount,
        public string $currency,
    ) {
    }
}

plus(self $other): self wirft bei abweichender Währung \InvalidArgumentException('Costs in different currencies cannot be added.'). jsonSerialize() rundet amount auf sechs Nachkommastellen.

CredentialStatus

php
final readonly class CredentialStatus
{
    /**
     * @param array{used: int, limit: ?int}|null $quota
     */
    public function __construct(
        public bool $valid,
        public string $message,
        public ?array $quota = null,
    ) {
    }
}

quota.limit darf bei einem unbegrenzten Tarif null sein. Die Administration und die CLI zeigen das dann als unbegrenzt.

Fehler

Nice\Translate\Provider\Exception\ProviderException ist die einzige Exception-Klasse in src/Provider/Exception/. Sie ist nicht final, deshalb können Sie davon ableiten.

php
class ProviderException extends \RuntimeException
{
    public function __construct(
        private readonly string $userSafeMessage,
        public readonly bool $retryable = false,
        ?\Throwable $previous = null,
        int $code = 0,
        private readonly bool $globalFailure = false,
        private readonly bool $bisectable = true,
    ) {
        parent::__construct($userSafeMessage, $code, $previous);
    }
}
FlagStandardWas es der Engine mitteilt
retryablefalseEin späterer Versuch kann erfolgreich sein
globalFailurefalseDer Fehler betrifft den gesamten Anbieter oder das Konto, und nicht eine einzelne Eingabe
bisectabletrueEin Aufteilen des fehlgeschlagenen Batches kann den problematischen Inhalt eingrenzen

Das erste Konstruktorargument ist die anwenderfreundliche Meldung. Die Erweiterung schreibt sie in nice_translate_job_error.message, zeigt sie in der Auftragsdetailansicht und gibt sie aus den Routen für Vorschau und Zugangsdaten-Test zurück. Schreiben Sie niemals einen API-Schlüssel, einen rohen Upstream-Body oder einen Stacktrace hinein. Das gehört in die $previous-Exception und in Ihr eigenes Logging.

Wählen Sie die Flags so, wie es die eingebaute HTTP-Basisklasse tut:

SituationretryableglobalFailurebisectable
Transportfehler (keine HTTP-Antwort)truetruefalse
HTTP 429, 500, 502, 503, 529truetruefalse
HTTP 400, 413, 422falsefalsetrue
HTTP 401, 402, 403, 404falsetruefalse

Die Engine wirft außerdem Nice\Translate\Engine\Exception\PlaceholderLostException, wenn ein geschützter Platzhalter eine Übersetzung nicht übersteht. Diese Exception werfen Sie nie selbst. Sie ist der Grund, warum ein Feld mit veränderten ⟦n⟧-Tokens scheitert und ungeschrieben bleibt.

Was die Engine mit einer geworfenen ProviderException macht

EntityTranslator::translateChunk() fängt die Exception ab und richtet sich nach den Flags:

  1. Bisektion. Die Engine halbiert den Chunk und versucht jede Hälfte erneut, wenn der Fehler nicht global ist, wenn die Exception bisektierbar ist (oder gar keine ProviderException ist), wenn der Chunk mehr als einen Text enthält und wenn die Rekursionstiefe unter vier liegt. Nur ein Datensatz, der nach der Bisektion weiterhin scheitert, gilt als fehlgeschlagen.
  2. Fehler je Datensatz. Nach dem Ende der Bisektion erhält jeder Eintrag des Chunks die Meldung der Exception als Fehler. Ein Throwable, das keine ProviderException ist, gibt die allgemeine Meldung „The translation provider request failed.“
  3. Ein Stopp für weitere Aufrufe. Wenn isGlobalFailure() wahr ist, ruft der Batch den Anbieter nicht weiter auf, und die verbleibenden Chunks scheitern mit derselben Meldung. Ein Schlüssel, den jemand mitten im Lauf widerruft, kostet deshalb einen fehlgeschlagenen Aufruf für den gesamten Batch.
  4. Erneute Zustellung. Beim Managed-Anbieter gibt die Engine einen wiederholbaren oder lokal unklaren Fehler mit derselben Folge von Idempotenzschlüsseln an Messenger zurück. Bei BYOK-Anbietern ist retryable innerhalb eines Auftrags überwiegend informativ: Der fehlgeschlagene Chunk wird zu Fehlern je Datensatz, und der Händler führt sie aus der Auftragsdetailansicht heraus erneut aus.

Registrierung

Taggen Sie Ihren Service mit nice_translate.provider. ProviderRegistry erhält einen getaggten Iterator, deshalb brauchen Sie keinen Compiler Pass und keine weitere Konfiguration:

xml
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
    <services>
        <service id="Acme\Translate\Provider\AcmeProvider">
            <argument type="service" id="http_client"/>
            <argument type="service" id="Shopware\Core\System\SystemConfig\SystemConfigService"/>
            <tag name="nice_translate.provider"/>
        </service>
    </services>
</container>

Zum Vergleich: Die sechs eingebauten BYOK-Anbieter erhalten http_client, SystemConfigService, logger und LocaleMapper, in dieser Reihenfolge.

Wie Ihr Anbieter auf die Einstellungsseite kommt

Der Tab Anbieter der Übersetzungseinstellungen rendert eine Karte je registriertem Anbieter außer managed. Er leitet die Konfigurationsschlüssel aus der Anbieter-ID ab:

Feld der KarteSystem-Config-Schlüssel
API-SchlüsselNiceTranslate.config.<providerId>ApiKey
ModellNiceTranslate.config.<providerId>Model

Ein Anbieter mit der ID acme bekommt deshalb eine Karte, die ihren Schlüssel unter NiceTranslate.config.acmeApiKey speichert. Lesen Sie diesen Schlüssel in isConfigured(), dann funktionieren das Kennzeichen Konfiguriert und die Schaltfläche Verbindung testen. Die Modellauswahl füllt sich nur, wenn getModels() oder eine Live-Abfrage Einträge liefert.

Achtung

Die Einstellungsseite der Erweiterung hat keine Felder für Optionen jenseits von API-Schlüssel und Modell. Jede weitere Konfiguration, die Ihr Anbieter braucht, muss aus der Konfiguration Ihres eigenen Plugins kommen.

Routing

Nach der Registrierung nimmt Ihr Anbieter an der üblichen Auflösung teil. ProviderRegistry löst eine Zielsprache über languageProviderMap auf, danach über defaultProvider, danach über die Fallback-ID deepl. Die Hintergrund-Automatisierung nutzt ihre ausdrückliche Übersteuerung autoTranslateProviderId, wenn Sie eine setzen, und ansonsten dieselbe Kette. Siehe Automatisierung.

Ein vollständiges Beispiel

Das folgende Beispiel zeigt einen kompletten Anbieter für einen internen maschinellen Übersetzungsdienst. Er implementiert das Interface direkt, sodass jede Methode hier zu sehen ist.

php
<?php declare(strict_types=1);

namespace Acme\Translate\Provider;

use Nice\Translate\Provider\Dto\Cost;
use Nice\Translate\Provider\Dto\CredentialStatus;
use Nice\Translate\Provider\Dto\ProviderRequest;
use Nice\Translate\Provider\Dto\ProviderResult;
use Nice\Translate\Provider\Exception\ProviderException;
use Nice\Translate\Provider\TranslationProviderInterface;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Symfony\Contracts\HttpClient\Exception\ExceptionInterface as HttpException;
use Symfony\Contracts\HttpClient\HttpClientInterface;

final class AcmeProvider implements TranslationProviderInterface
{
    private const ENDPOINT = 'https://mt.acme.example/v1';
    private const CONFIG_KEY = 'NiceTranslate.config.acmeApiKey';
    private const USD_PER_MILLION_CHARACTERS = 8.0;

    public function __construct(
        private readonly HttpClientInterface $httpClient,
        private readonly SystemConfigService $config,
    ) {
    }

    public function getId(): string
    {
        return 'acme';
    }

    public function getLabel(): string
    {
        return 'Acme MT';
    }

    public function isConfigured(): bool
    {
        return $this->apiKey() !== '';
    }

    public function translate(ProviderRequest $request): ProviderResult
    {
        $texts = array_values($request->texts);
        if ($texts === []) {
            return new ProviderResult([], 0);
        }

        $apiKey = $this->apiKey();
        if ($apiKey === '') {
            throw new ProviderException(
                'Acme MT is not configured. Please add an API key in the settings.',
                globalFailure: true,
                bisectable: false,
            );
        }

        try {
            $response = $this->httpClient->request('POST', self::ENDPOINT . '/translate', [
                'headers' => ['Authorization' => 'Bearer ' . $apiKey],
                'json' => [
                    'texts' => $texts,
                    'source' => $request->sourceLocale,
                    'target' => $request->targetLocale,
                    'html' => $request->format === 'html',
                ],
            ]);
            $status = $response->getStatusCode();
            $payload = json_decode($response->getContent(false), true);
        } catch (HttpException $exception) {
            throw new ProviderException(
                'Acme MT could not be reached. Please try again later.',
                retryable: true,
                previous: $exception,
                globalFailure: true,
                bisectable: false,
            );
        }

        if ($status >= 400 || !\is_array($payload)) {
            $contentProblem = \in_array($status, [400, 413, 422], true);

            throw new ProviderException(
                $this->statusMessage($status),
                retryable: $status === 429 || $status >= 500,
                globalFailure: !$contentProblem,
                bisectable: $contentProblem,
            );
        }

        $translations = array_map(strval(...), (array) ($payload['translations'] ?? []));
        if (\count($translations) !== \count($texts)) {
            throw new ProviderException('Acme MT returned an unexpected number of translations.');
        }

        $characters = 0;
        foreach ($texts as $text) {
            $characters += mb_strlen($text);
        }

        return new ProviderResult(
            texts: array_values($translations),
            characters: $characters,
            cost: $this->estimate($characters),
        );
    }

    public function validateCredentials(?string $apiKey = null): CredentialStatus
    {
        $key = $apiKey !== null && trim($apiKey) !== '' ? trim($apiKey) : $this->apiKey();
        if ($key === '') {
            return new CredentialStatus(false, 'No API key configured.');
        }

        try {
            $status = $this->httpClient->request('GET', self::ENDPOINT . '/languages', [
                'headers' => ['Authorization' => 'Bearer ' . $key],
            ])->getStatusCode();
        } catch (HttpException) {
            return new CredentialStatus(false, 'Acme MT could not be reached.');
        }

        return $status < 400
            ? new CredentialStatus(true, 'API key is valid.')
            : new CredentialStatus(false, $this->statusMessage($status));
    }

    public function getModels(): array
    {
        return [];
    }

    public function supports(string $feature): bool
    {
        return $feature === 'html';
    }

    public function estimate(int $characters): Cost
    {
        return new Cost(($characters / 1_000_000) * self::USD_PER_MILLION_CHARACTERS, 'USD');
    }

    public function getMaxBatchSize(): int
    {
        return 50;
    }

    public function getMaxChunkBytes(): int
    {
        return 80000;
    }

    private function apiKey(): string
    {
        $value = $this->config->get(self::CONFIG_KEY);

        return \is_scalar($value) ? trim((string) $value) : '';
    }

    private function statusMessage(int $status): string
    {
        return match (true) {
            $status === 401, $status === 403 => 'Acme MT rejected the configured credentials.',
            $status === 429 => 'The Acme MT rate limit was reached. Please try again later.',
            $status >= 500 => 'Acme MT is temporarily unavailable. Please try again later.',
            default => 'Acme MT rejected the request.',
        };
    }
}

Die abstrakten Basisklassen wiederverwenden

Die Erweiterung liefert zwei abstrakte Anbieter mit. Sie können von einem davon ableiten, statt das Interface von Grund auf zu implementieren:

BasisklasseWas sie Ihnen abnimmt
AbstractHttpProviderEinen JSON-Request-Helfer mit begrenzten Wiederholungen (3 Versuche bei 429/500/502/503/529), Unterstützung für Retry-After mit einer Obergrenze von 60 Sekunden, exponentiellem Fallback, Abbildung von HTTP-Fehlern auf anwenderfreundliche ProviderExceptions und ein Logging, das nur Metadaten erfasst und niemals Schlüssel oder Antwort-Bodys. Sein Konstruktor erwartet HttpClientInterface, SystemConfigService und LoggerInterface; getMaxChunkBytes() liefert standardmäßig 80000
AbstractLlmProviderErweitert die obige Klasse um den gemeinsamen Übersetzungs-Prompt und einen strikten Antwortvertrag {"translations": string[]} mit einer korrigierenden Wiederholung. supports() liefert true für html, tone und glossary; getMaxBatchSize() ist 12 und getMaxChunkBytes() ist 24000

Beide Klassen haben einen Helfer configString(string $key): string, der aus der Domain NiceTranslate.config. liest. Behandeln Sie beide Klassen als interne Bausteine. TranslationProviderInterface ist der veröffentlichte Vertrag und der einzige Teil davon, bei dem Sie mit Stabilität rechnen können.

Batching und Chunking

Die Engine ruft translate() nie mit einer unbegrenzten Liste auf. Sie gruppiert die gesammelten Texte nach Format (html und text gehen in getrennte Anfragen) und teilt jede Gruppe danach nach zwei Regeln in Chunks:

  • kein Chunk enthält mehr Texte als getMaxBatchSize(), und
  • die kumulierte Bytelänge eines Chunks bleibt innerhalb von getMaxChunkBytes(), das EntityTranslator::CHUNK_BYTE_LIMIT (80 000 Bytes) zusätzlich deckelt.

Ein einzelner Text, der länger als das Bytebudget ist, wird trotzdem zu seinem eigenen Chunk. Die Grenze gilt für die Ansammlung, und einen einzelnen Wert teilt sie nicht auf.

Zum Vergleich melden die eingebauten Anbieter:

AnbietertypgetMaxBatchSize()getMaxChunkBytes()
DeepL5080 000
Google Translate10080 000
Die vier LLM-Anbieter1224 000

Die Werte für die LLM-Anbieter sind bewusst konservativ gewählt. Eine strikte JSON-Array-Ausgabe wird bei großen Batches unzuverlässig, und das Bytebudget hält die erwartete Ausgabe unter der maximalen Ausgabe-Token-Grenze des Modells. Leiten Sie Ihre eigenen Werte aus der Ausgabe ab, die Ihr Upstream unter Last unversehrt zurückgibt. Das dokumentierte Maximum liegt meist deutlich über dem Wert, der in der Praxis hält.

Die Einstellung batchSize der Erweiterung ist eine andere Grenze, eine Ebene über diesen beiden. Sie steuert, wie viele Datensätze in eine Queue-Nachricht gehen. Ihre beiden Grenzwerte steuern, wie viele Texte innerhalb dieser Nachricht in eine Upstream-Anfrage gehen.

Optional: Live-Modellliste

Wenn Ihr Anbieter seine derzeit verfügbaren Modelle über seine API auflisten kann, implementieren Sie zusätzlich Nice\Translate\Provider\SupportsLiveModelsInterface:

php
public function fetchLiveModels(?string $apiKey = null): array;

Folgen Sie dem Vertrag, den die vier eingebauten LLM-Anbieter implementieren. Führen Sie die Live-IDs mit Ihren kuratierten Metadaten zusammen, sodass bei übereinstimmender ID die kuratierten Werte für name, tier und pricing gewinnen. Stellen Sie die kuratiert bekannten Modelle in kuratierter Reihenfolge nach vorn, und sortieren Sie den Rest alphabetisch absteigend. Begrenzen Sie das Ergebnis auf 30 Modelle. Setzen Sie source: 'live' bei jedem Eintrag. Ein ausdrücklich übergebener $apiKey hat Vorrang vor dem gespeicherten Konfigurationsschlüssel, deshalb aktualisiert die Administration die Liste auch mit einem eingegebenen, aber nicht gespeicherten Schlüssel. Werfen Sie nach einem Fehler eine ProviderException.

Für eine Live-Liste weitet sich die Union der Stufen zu 'flagship'|'quality'|'balanced'|'budget'|'legacy'|null.

GET|POST /provider/{providerId}/models versucht die Live-Abfrage, wenn der Anbieter das Interface implementiert und die Anfrage einen Schlüssel übergeben hat oder isConfigured() true liefert. Jedes Throwable fällt still auf getModels() zurück, und das Feld source der Antwort meldet, welche Liste Sie erhalten haben. Siehe Admin-API.

modernice Extensions für Shopware 6. Shopware ist eine Marke der shopware AG — diese Dokumentation steht in keiner Verbindung zur shopware AG.