Files
tibi-types/schemas/config/embedding.schema.json
T
apairon 1233bd393d fix(schema): latencyWindow ist ein String, duration-Muster folgt der Go-Grammatik
Review-Nachbefunde am neuen embedding-Schema:
- latencyWindow war als integer/number deklariert, ist in Go aber ein String
  (ConfigEmbeddingGroup.OnSingleQuery.LatencyWindow, per Atoi geprüft) — Schema
  und index.d.ts auf digits-String (>0) umgestellt.
- duration-Pattern erlaubte nur eine Einheit und akzeptierte Unsinn wie "1ss":
  jetzt (Zahl+Einheit)+, damit auch 1m30s/1h1m/1.5h gelten und 1ss/8/abc fallen.
- Beispiel examples/server/embedding.yml übt latencyWindow und Mehrfach-Einheiten
  aus (npm test: 5 YAML gegen 17 Schemas, tsc --noEmit --strict grün).
2026-10-17 14:07:30 +00:00

256 lines
9.3 KiB
JSON

{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "embedding.schema.json",
"title": "Tibi server config — embedding section (server-level, not project config)",
"description": "Der Abschnitt `embedding` aus der Server-config.yml (server.embedding): Provider + Failover-Gruppen + Gold-Datei-Pfad. Quelle der Wahrheit: internal/config/server.go und internal/config/embedding_group.go im tibi-server.",
"type": "object",
"additionalProperties": false,
"properties": {
"providers": {
"type": "array",
"items": {
"$ref": "#/definitions/provider"
}
},
"groups": {
"type": "array",
"items": {
"$ref": "#/definitions/group"
}
},
"goldenPath": {
"type": "string",
"description": "Verzeichnis der Gold-Dateien für die Mitglieds-Prüfung (Vorgabe: testdata/embedding-golden relativ zum Arbeitsverzeichnis). Erzeugen mit cmd/embed-probe-golden. Ohne verankerte Gold-Datei prüft die Gruppe nur gegen ihre Referenz."
}
},
"definitions": {
"duration": {
"type": "string",
"description": "Go-Duration (time.ParseDuration), z. B. 120s, 800ms, 25s, 1m30s, 1.5h",
"pattern": "^([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$"
},
"provider": {
"type": "object",
"additionalProperties": false,
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "Provider-Name; Sammlungen referenzieren ihn (oder eine Gruppe) über vector.provider."
},
"type": {
"type": "string",
"enum": [
"native",
"flagembedding"
],
"description": "native = ONNX im Prozess (braucht -tags cgo_embedding), flagembedding = externer HTTP-Endpunkt. Ohne Angabe: native."
},
"model": {
"type": "string"
},
"modelPath": {
"type": "string",
"description": "ONNX-Modellverzeichnis (type: native)."
},
"dimensions": {
"type": "integer",
"minimum": 1
},
"baseURL": {
"type": "string",
"description": "Embedding-Endpunkt (type: flagembedding)."
},
"apiKey": {
"type": "string"
},
"documentPrefix": {
"type": "string"
},
"queryPrefix": {
"type": "string"
},
"timeoutPerAttempt": {
"$ref": "#/definitions/duration",
"description": "flagembedding: HTTP-Timeout (Vorgabe 120s); native: Schranke je Embed-Aufruf (nur am Aufruf-/Stufen-Eintritt wirksam, ONNX-Runs sind nicht unterbrechbar). Ungültige oder nicht-positive Werte werden ignoriert (Warnung im Log)."
},
"slowThreshold": {
"$ref": "#/definitions/duration",
"description": "Nur gruppenwirksam: langsamere Aufrufe zählen für lowest-latency als langsam."
},
"revision": {
"type": "string",
"description": "Gewichts-/Build-Tag (Datei-Hash, Quantisierung); fließt in die Modell-Identität ein (Gruppen-Prüfung, Query-Cache)."
},
"sparse": {
"type": "boolean",
"description": "false = Endpunkt/Modell liefert keine Sparse-Vektoren: native lädt die Sparse-Session nicht, flagembedding schweigt die einmalige Dense-only-Warnung; in einer Gruppe lehnt die Prüfung abweichende Sparse-Formen ab."
}
}
},
"group": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"members"
],
"properties": {
"name": {
"type": "string",
"description": "Gruppenname; darf kein Provider-Name sein und darf nur einmal vorkommen."
},
"description": {
"type": "string"
},
"members": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {
"type": "string"
},
"description": "Provider-Namen der Gruppe. Alle Mitglieder müssen dieselbe Modell-Identität liefern (Dimensionen, Sparse-Unterstützung, revision)."
},
"onSingleQuery": {
"type": "object",
"additionalProperties": false,
"properties": {
"policy": {
"type": "string",
"enum": [
"prefer-in-order",
"fastest-wins",
"round-robin",
"lowest-latency",
"weighted"
]
},
"preferredOrder": {
"description": "Bevorzugte Reihenfolge; als Komma-String oder YAML-Liste. Mitglieder, die fehlen, hängen in Config-Reihenfolge hinten an.",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"timeoutPerAttempt": {
"$ref": "#/definitions/duration",
"description": "Schranke je Versuch. Der bevorzugte Member läuft genau darin; Ausweich-Member bekommen max(Provider-Timeout, dieser Wert)."
},
"timeoutTotal": {
"$ref": "#/definitions/duration",
"description": "Gesamt-Deadline je Aufruf; muss >= timeoutPerAttempt sein."
},
"autoFailback": {
"type": "boolean",
"description": "true (Vorgabe) = jeder Aufruf beginnt wieder beim bevorzugten Mitglied; false = bleibt beim letzten Gewinner."
},
"raceDelay": {
"$ref": "#/definitions/duration",
"description": "Nur policy fastest-wins: Staffelung zwischen den Teilnehmern."
},
"raceMaxFanout": {
"type": "integer",
"minimum": 0,
"description": "Nur policy fastest-wins: maximale parallele Teilnehmer (0 = alle)."
},
"weights": {
"type": "array",
"items": {
"type": "integer",
"minimum": 0
},
"description": "Nur policy weighted: relative Gewichte je Mitglied, positional zur members-Liste."
},
"latencyWindow": {
"type": "string",
"pattern": "^[0-9]+$",
"description": "Fenster für die EWMA-Latenz (lowest-latency) als Ziffern-String (> 0); Go-Feld ist ein String, der per Atoi geprüft wird."
},
"retryAttempts": {
"type": "integer",
"minimum": 0
},
"retryBackoff": {
"$ref": "#/definitions/duration"
},
"retryBackoffMax": {
"$ref": "#/definitions/duration"
},
"hedgeAfterSlow": {
"$ref": "#/definitions/duration",
"description": "prefer-in-order: nach dieser Dauer starten die übrigen Mitglieder parallel (Hedge)."
}
}
},
"onBulkWrite": {
"type": "object",
"additionalProperties": false,
"properties": {
"distribution": {
"type": "string",
"enum": [
"round-robin",
"lowest-latency",
"weighted"
],
"description": "Verteilung der Batch-Ranges über die Mitglieder; ohne Angabe kein Sharding (serieller Schreibpfad). Jeder Range bleibt ganz auf einem Mitglied."
},
"maxConcurrentTotal": {
"type": "integer",
"minimum": 0
},
"maxConcurrentPerEndpoint": {
"type": "integer",
"minimum": 0,
"description": "Pro Mitglied; lokale (native) Mitglieder sind auf 1 begrenzt."
},
"timeoutPerAttempt": {
"$ref": "#/definitions/duration",
"description": "Bulk-Schranke je Versuch; pro Mitglied gilt max(Provider-Timeout, dieser Wert)."
},
"timeoutTotal": {
"$ref": "#/definitions/duration"
}
}
},
"health": {
"type": "object",
"additionalProperties": false,
"properties": {
"checkInterval": {
"$ref": "#/definitions/duration",
"description": "Abstand der Health-Probes. > 0 aktiviert zusätzlich den Late-Join: quarantänisierte Mitglieder (und offline Gruppen) werden gegen die aktuelle Referenz neu geprüft und kommen zurück, sobald sie passen. 0 = nur Neustart hilft."
},
"checkTimeout": {
"$ref": "#/definitions/duration",
"description": "Schranke je Probe-Embed; die Rundenschranke beim Start skaliert mit der Zahl der Probe-Texte."
},
"unhealthyAfterFailures": {
"type": "integer",
"minimum": 0
},
"healthyAfterSuccesses": {
"type": "integer",
"minimum": 0
},
"cooldownAfterUnhealthy": {
"$ref": "#/definitions/duration",
"description": "Pause, bevor ein offenes Mitglied wieder halb geöffnet wird (danach genügt Verkehr, kein Probe-Lauf)."
}
}
}
}
}
}
}