Components
TL;DR
- Ein component ist die Einheit der Fähigkeit der Plattform: ein typisiertes, versioniertes, containerisiertes Stück Code, das eine Aufgabe erledigt — ein Bild lesen, einen Detector ausführen, einen Audio-Frame normalisieren, in eine Queue schreiben. Es ist über viele backends wiederverwendbar und gehört einem Team.
- Ein component deklariert, was es konsumiert, was es produziert und wie es konfiguriert werden kann — typisierte input streams, typisierte output streams, typisierte Parameter, optionale file-Slots. Diese Deklaration ist der Vertrag, den jede andere Schicht der Plattform liest.
- Components haben unveränderliche releases. Jedes Publish erzeugt eine prerelease; promote verwandelt eine prerelease in eine released version. Backends pinnen bestimmte releases, sodass promote kein laufendes deployment ändert.
- Ein component besitzt Verhalten. Ein backend besitzt Komposition. Ein deployment besitzt runtime. Die drei Primitive getrennt zu halten ist das, was eine einzelne Fähigkeit viele Produkte antreiben lässt, ohne den Code zu forken.
- Die meisten components sind stateless Funktionen: Input rein, Output raus. State, virtual streams und custom Pull-Policies sind fortgeschrittene Themen für die Fälle, die sie brauchen; die Grundform deckt die meisten echten components ab.
Was ein component tatsächlich ist
Ein component ist die kleinste wiederverwendbare Einheit von Fähigkeit, die die Plattform modelliert. Konkret ist es eine typisierte Funktion, verpackt als containerisierter worker, mit einem Manifest, das deklariert, was es konsumiert, was es produziert und wie es konfiguriert werden kann. Diese Deklaration — die component.yml — ist der Vertrag, den jede andere Schicht liest: backends nutzen ihn, um die Graph-Verdrahtung vor dem Deploy typzuprüfen, der Katalog nutzt ihn für die Discovery, das runtime nutzt ihn, um zu bestimmen, was der Container des worker erwartet.
Die Granularität ist beabsichtigt. Eine bloße Funktion ist zu klein, um unabhängig zu operieren: kein Manifest, kein Container, keine Versionierung. Eine ganze Pipeline ist zu groß, um über Produkte hinweg wiederverwendet zu werden. Ein component sitzt auf der Ebene, auf der eine Aufgabe auf eine Einheit abbildet, die gepinnt, validiert, wiederverwendet, getauscht und für sich operiert werden kann. Der typisierte Vertrag und das unveränderliche release-Modell sind das, was diese Einheit komponierbar macht statt nur isoliert.
Der Standardvertrag ist, dass components an der Graph-Grenze stateless sind. Die Funktion konsumiert typisierte Inputs und produziert typisierte Outputs; alles andere — Model-Weights, Konfiguration, optionaler state, Side-Channel-Events — ist Maschinerie, die das component deklariert, wenn es sie braucht. Dieser Default hält die meisten components klein und lässt den Scheduler sie frei parallelisieren. Die Opt-ins existieren für die Fälle, die sie brauchen, und jedes davon schränkt ein, wie das runtime die vertex schedulen kann, was der richtige Druck ist, um components einfach zu halten.
Mentales Modell
┌──── component release (immutable) ─────┐
│ │
│ component.yml src/ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ language │ │ Python or │ │
│ │ inputs[] │ │ C++ code │ │
│ │ outputs[] │ │ │ │
│ │ config │ │ implements │ │
│ │ files │ │ the typed │ │
│ │ tags │ │ function │ │
│ └─────────────┘ └─────────────┘ │
│ │
└────────────────────┬───────────────────┘
│ pinned by
▼
backend vertex
Die release ist das unveränderliche Artefakt. Ein backend pinnt sie als vertex, bindet ihre Parameter und files und verdrahtet ihre Streams. Dieselbe release kann als vertex in vielen backends sitzen; jedes backend trägt seine eigenen Bindungen und seinen eigenen Graph-Kontext. Promote zeichnet eine neue released-Zeile auf; das Pinnen ist das, was ein backend davon isoliert — ein backend, das release_v3 gepinnt hat, läuft weiter v3, nachdem v4 default geworden ist.
Siehe Backends für den Graphen, in den sich die release einklinkt, und Types für den typisierten-Stream-Vertrag, den ihre Inputs und Outputs erfüllen.
Was dir "typisierte Funktion" tatsächlich bringt
Verwende diesen Rahmen das erste Mal, wenn du fragst "was garantiert der component-Vertrag".
Der deklarierte typisierte Vertrag ist im Manifest klein — ein paar Zeilen pro Stream — und überall sonst tragend. Die Typ-Inferenz des backend nutzt ihn, um abzulehnen, einen Image-Output in einen [BoundingBox]-Input zu verdrahten. Die CLI nutzt ihn, um sinnvolle Fehlermeldungen zu rendern, wenn ein Parameter fehlt. Der Katalog nutzt ihn, um components nach den Modalitäten zu filtern, die sie verarbeiten. Das runtime nutzt ihn, um zu bestimmen, welche Container-Ressourcen das component beim Start braucht.
Der component-Autor schreibt den Vertrag einmal; jede andere Partei — Graph-Autoren, nachgelagerte Konsumenten, Ops-Teams, spätere Maintainer — liest ihn. Das Deklarieren des Vertrags verschiebt Verdrahtungs- und Parameterfehler zur Bearbeitungszeit, wo die Typ-Inferenz des backend sie fängt, statt sie auf einen fehlgeschlagenen Deploy zu vertagen. Das ist der praktische Grund, warum das Manifest verpflichtend statt optional ist.
Die typisierten Werte selbst stammen aus einer von drei Quellen: pipelang-atomic types (Double, String, Int64), composite types, aus ihnen gebaut ([BoundingBox], Maybe<String>, (Image, [Double])), oder named types aus der Type-Registry der Plattform (Image, Tensor, AudioFrame, BoundingBox). Die named types tragen Domänensemantik: ein Image ist nicht nur ein Tensor von Bytes, es ist der typisierte Wert, auf den sich jedes bildverarbeitende component einigt, mit Shape, Channel-Order und Metadaten, die nachgelagerte Konsumenten direkt lesen statt neu abzuleiten.
Siehe Named types für die Registry, die Image, Tensor und den Rest stützt.
Stateless als Default, stateful wenn das Verhalten Historie braucht
Verwende den Default; greife nur dann nach state, wenn der nächste Output wirklich von den vorherigen abhängt.
Das Standard-component ist eine stateless Funktion: das runtime kann sie für dieselbe vertex viele Male nebenläufig aufrufen, weil kein Aufruf von einem anderen abhängt. Das lässt den Scheduler eine einzelne vertex unter Last über CPU-Kerne fan-outen oder unabhängige Invocations parallel laufen, ohne Ordering zu koordinieren.
Das Deklarieren von state ändert diesen Vertrag. Das component bittet die Plattform, Memory zwischen Aufrufen zu persistieren und die Funktion pro vertex eine zur Zeit aufzurufen, sodass der next state des vorherigen Aufrufs für den nächsten sichtbar ist. Die vertex wird history-abhängig: Tracker, gleitende Windows, Session-Maps, Conversation-Memory, Debouncing und "emittiere erst nach N Sekunden gepufferten Audios" brauchen alle state, um korrekt zu funktionieren. Eine stateful vertex ist seriell pro Replica, was der richtige Druck ist, um state aus components herauszuhalten, die ihn nicht brauchen.
Was außerhalb des persistierten state bleibt, ist die per-process-Maschinerie: Model-Handles, Datenbank-Clients, kompilierte Regexes, Caches, die beim Start regeneriert werden können. Diese gehören in Module-Globals oder Setup-Hooks, nicht in den persistierten state der Plattform. Persistierter state ist für Verhaltens-Memory — die Werte, deren Verlust zwischen Aufrufen verändern würde, was der nächste Aufruf emittiert. Alles andere ist process-lokal und lebt entsprechend.
Side-Effects leben hinter virtual streams
Verwende dies, wenn das component mit der Welt außerhalb des Graphen sprechen muss: HTTP, WebSocket, Devices, SDK-Callbacks, file-Writes mit Retry-Semantik.
Die meisten components konsumieren aus Graph-Inputs, führen eine deterministische Funktion aus und emittieren an Graph-Outputs. Manche components haben eine schwierigere Aufgabe: sie besitzen einen HTTP-Listener, der Traffic asynchron empfängt, einen Kamera-Thread, der Frames unabhängig vom Graph-Timing erzeugt, ein LLM-SDK, das Tokens durch einen Callback zurückstreamt, einen file-Writer, der Retry-Handling braucht. Diese Side-Effects direkt in die graph-zugewandte Funktion einzufädeln macht es dem Scheduler unmöglich zu bestimmen, wann die Funktion sicher aufzurufen ist.
Virtual streams isolieren diese Arbeit. Virtual input ist eine component-lokale Queue, befüllt von Code außerhalb des normalen graph-zugewandten Ticks: einem HTTP-Listener, einem Device-Thread, einem Streaming-SDK-Callback. Das component kann virtual input in seinen pull request aufnehmen, sodass diese externen Events weiterhin durch dieselbe scheduler-sichtbare Funktion wie normale Stream-Nachrichten fließen. Virtual output ist das Inverse: eine Queue von Side-Effect-Arbeitselementen, die das component aus einem Tick zurückliefert, die ein bridge-Thread asynchron ausführt und Ergebnisse zurück durch virtual input speisen kann.
Die Disziplin, die virtual streams funktionieren lässt, ist, dass die graph-zugewandte Funktion scheduler-sichtbar bleibt. Side-Effects leben hinter den Queues; die Funktion liest und schreibt sie; die bridge führt die IO aus. Diese Trennung ist das, was ein component mit der Außenwelt sprechen lässt, während der Scheduler sein Modell behält, wann die Funktion läuft.
Releases sind unveränderlich; promote ist bewusst
Verwende dieses mentale Modell, wann immer du darüber nachdenkst, "warum hat mein backend den neuen Code nicht aufgenommen".
Jedes Publish erzeugt eine prerelease: einen privaten validierten Build, den der Aufrufer in Test-backends deployen kann. Promote ist der separate, bewusste Schritt, der eine prerelease in eine released version verwandelt: das Image geht in die Registry, die in component.yml deklarierten Tags bewegen sich (latest, default), und die version wird für jeden im Audience-Bereich des workspace deploybar. Die version-ID ändert sich nicht; nur ihr Zustand. Promote wird abgelehnt, bis die version jede Regel der Release-Checkliste erfüllt.
Backends pinnen bestimmte release-IDs. Das Promoten einer prerelease ändert die gepinnte version keines deployten backend — das backend läuft weiter, was auch immer es zum Zeitpunkt des letzten Deploy gepinnt hat. Ein backend auf eine neuere release vorwärtszurollen ist eine explizite vertex-Mutation, gefolgt von einem Redeploy. Diese Trennung ist der Grund, warum das Promoten einer kaputten version für sich allein kein laufendes backend lahmlegen kann. Das laufende backend ist gepinnt; die neue release ist ein neues adressierbares Artefakt im Katalog.
Siehe Publish-Semantik für die volle publish + promote-Schleife.
Was ein component besitzt und was nicht
Ein component besitzt seine Identität (Display-Name plus workspace-skopierten Slug), seine Sprache (Python oder C++), seine deklarierten input- und output-Stream-Typen, sein typisiertes config_schema, sein optionales file_schema für Model- und Daten-Slots, sein optionales generated_file_schema für Outputs, die es als workspace-files zurück-committet, und seinen Implementierungs-Quellcode unter src/. Es besitzt auch seine Discovery-Metadaten — die Katalog-Kategorie und Modalitäts-Tags, die README und AGENT.yml, die mit der release reisen.
Es besitzt nichts Graph-Förmiges. Das backend, in dem es auftritt, die vertex-Bindungen seiner Parameter in diesem backend, die file-IDs, die in seine Slots gebunden sind, die endpoint-Aliase, durch die seine Outputs offengelegt werden, das deployment, das es ausführt, das runtime, das das deployment hostet — all das sind backend-und-deployment-Anliegen. Das component ist die Fähigkeit; alles andere ist Komposition und runtime.
Diese Trennung ist der ganze Grund, warum ein einzelnes component viele Produkte antreiben kann. Ein Detector-component ist dasselbe in einem Lagerhaus-Sicherheits-backend, einem Retail-Traffic-backend und einem klinischen Bildgebungs-backend; jedes backend verdrahtet es anders, bindet andere Parameter, deployt es auf anderem Compute. Das component muss nichts davon wissen.
Die Release-Checkliste
Ein gültiges component typprüft; ein gutes component ist allein aus seinem Manifest heraus vertrauenswürdig. Die Release-Checkliste ist diese Latte, schriftlich festgehalten, und die Plattform misst jede version an ihr: promote wird abgelehnt, solange eine Regel open ist oder fehlschlägt. Components, die sie erfüllen, können von Menschen — und Agents — verdrahtet, getauscht und betrieben werden, die die Implementierung nie gelesen haben.
Jede Regel wird am aktuellen Code der version beurteilt und sagt, wie gezeigt wird, dass sie erfüllt ist. Auto-Regeln berechnet die Plattform bei jedem Lesen aus dem Code. Live-Regeln bestehen nur bei einem erfolgreichen Live-Test genau dieses Codes, der einen Runner ausgeführt hat; ein Lauf, der die version nur deployt hat, belegt keine. Review-Regeln werden mit Belegen aufgezeichnet, die ein Reviewer prüfen kann — Datei und Zeile, die gemessene Zahl, der beobachtete Output. Bedingte Regeln dürfen für ein component ohne das Feature, das sie betreffen, not_applicable sein, und eine Person darf eine Regel mit Begründung erlassen (waive). Das Publishen von neuem Code setzt jede live- und review-Regel wieder auf open.
Vertrag
contract.first(review) — contract designed before code. component.yml — inputs, outputs, configuration and file slots — is designed before the implementation and stays the source of truth. Graph authors, the catalog and agents read only the contract; code shaped first and described afterwards leaks its accidents into it. Meet it: Write component.yml from the task, the input and output shapes and the downstream consumers, then implement against it. Evidence: the contract decisions and why each port, parameter and slot has the shape it has.contract.exact(live) — declared types match what the code emits. Every type the worker emits is declared in component.yml field for field, spelled and typed as the code writes it. A declared field the code never writes, or an emitted field the type does not declare, fails every item at run time. Meet it: Make the code write exactly the declared fields, or change the declaration to what the code means to emit. Proven by a recorded live test of the version in which every item passed type validation.contract.reuse(review) — built only where nothing existing fits. A component exists only for logic worth reusing across backends or needing files, secrets or releases. A released component that fits, a built-in transformation or an inline transform vertex makes a new component a duplicate the catalog has to carry forever. Meet it: Search the catalog by capability and by input and output types, and compare contracts, not names. Evidence: the searches run and why each near fit falls short.contract.standalone(review) — works and is described on its own. A component's contract and documents describe what it does with its own inputs and outputs. Another component appears only as an example of where its data can come from or go to, never as something it requires, because a graph author is free to wire it to anything that carries the type. Meet it: Remove wording that pairs the component with another one as a requirement; name the type it needs instead and keep other components to examples. Evidence: the summary and pick_when entries read without the paired component.contract.typed_params(review) — parameters carry their constraints in the type. Valid values live in the type, not in prose: fixed choices are a refined String such asString<"fast" | "accurate">, optional values a Maybe, so a graph author cannot bind a value the code rejects. Meet it: Refine each config_schema type to its valid values. Evidence: each parameter whose type is a bare String or number and why its whole range is valid.contract.secret_params(auto) — secrets are optional secret strings. A secret parameter isMaybe<String>, marked secret, defaulting toNothing (), so an omitted secret is absent rather than an empty string a deploy has to refuse. Meet it: Declare every secret astype: 'Maybe<String>',secret: true,default: 'Nothing ()'.contract.native_types(review) — registered types for platform data, and no one-field records. Images, audio, tensors, embeddings, masks, geometry, detections, depth and point clouds travel as the registered types, built through the SDK constructors. A hand-packed record type-checks today and breaks silently when the carrier evolves; a missing named type is a type-registry request, not an opaque inline record. Every custom record a port, parameter or state declares has at least two fields: a lone value travels as itself and a sequence of items as a list, never wrapped in a one-field custom record; a registered type travels as registered. Meet it: Emit and accept the registered types through the SDK, and unwrap any one-field custom record into the value it holds. Evidence: each port's, parameter's and state's type and, for any custom record, its fields and why no registered type carries it.contract.oneof(review, conditional) — output arms are projections of one result. When several output shapes are valid, the component computes one canonical result and projects it into eachoneofarm, choosing the arm once at startup from the connected sink type — never per message. Meet it: Implement one internal result and one projection per arm; resolve the arm at startup. Mark not applicable when no output is a oneof.contract.files(review, conditional) — model files come through declared file slots. Weights, datasets and other files bind throughfile_schemaslots that declare the real layout the worker loads. A worker that downloads at startup hides its real dependency, fails offline and cannot be pinned. A component serves a task family: another model its code can load through the same slot layout is another file in that slot, never another component, so the component's name, README, AGENT manifest and slot tag describe the task and the file format, never one model. A model's name, author attribution and license live on its file. Every fact that differs between models of the family (labels and the position of structural ones such as background, counts, shapes, input size, anchors, normalization, vocabulary) is read from the bound file, its metadata or a side file the slot's bundle layout declares; a fact no file carries is a parameter, defaulting to the library's own value where the library has one and required otherwise, never to one model's value, and an operating value the user may tune (a threshold) is a parameter whose default the file supplies when it carries one. The worker fails loudly when a fact is missing or disagrees with the file's tensors; the code never hard-codes one model's values. Code that fixes particular semantic classes for selection or special treatment belongs only to a component whose declared task names them; a class filter the user sets through a parameter is generic. Meet it: Declare a slot per file set with its real bundle layout and load only from it; read every per-model fact from the file, its metadata or a side file in the bundle, and take from a parameter only what no file carries and the operating values the user may tune, defaulting to the file's value when it carries one; keep model identity, attribution and license on the linked file. Evidence: each per-model fact the code uses and where it reads it. Mark not applicable when the component reads no files.contract.resources(live) — hardware needs declared and true. GPU, memory and architecture needs are declared, and the declaration is what the component actually ran on. An undeclared need surfaces as a crash on the first runtime that lacks it. Meet it: Declare the hardware the worker needs; prove it with a recorded live test on a runtime matching the declaration.
Verhalten
behavior.one_output(live) — one output per valid input. Every valid input yields exactly one value on every output port. Absence is said explicitly — a Nothing, an empty list, a documented value — never by skipping a tick, because downstream vertices schedule against each emission and a skipped tick stalls the graph. Meet it: Emit on every port for every input, with explicit absence. Proven by a recorded live test whose runner checks one output per input.behavior.fail_loud(review) — invariants asserted, failures passed on whole. Impossible states are asserted at the boundary and trusted inside; there is no catch-and-continue and no fallback value standing in for a missing real one. An upstream service's failure reaches the output with its complete error object, so the cause is visible where it surfaces. Meet it: Assert at the boundary, remove silent fallbacks, return the full upstream error. Evidence: the file and line of each boundary assertion and error path.behavior.logging(auto) — logs only through ppl::logger. A C++ component logs only through ppl::logger, so every line it writes reaches the platform's logs with its level and source. Its own C/C++ files under src/ (symlinks read through to what they name) do not use std::cout, std::cerr, std::clog, printf, wprintf (and their v-forms), puts, putchar, perror, dprintf / write / writev on descriptor 1 or 2, std::print / std::println / std::vprint_unicode with a format string first, or the stdout / stderr handles (fprintf(stderr, …), fputs(…, stdout)). Comments and string literals are not checked; the check is lexical and does not expand macros or resolve symbols. A directory under src/ that carries its own license file — named LICENSE, LICENCE, COPYING or UNLICENSE, alone or followed by.and a suffix (LICENSE.txt, LICENSE.hnswlib) — is vendored third-party code and is exempt with everything below it. A C++ component that breaks the rule is refused at upload. Meet it: Replace each write with ppl::logger::log (info) or ppl::logger::elog (error) from "pipelogic/logger/log.hpp", or ppl::logger::dlog (debug) from "pipelogic/logger/dlog.hpp". Vendor third-party code in its own directory under src/ together with its license file.behavior.no_stall(live) — never blocks a shared stream. The component never holds a stream other vertices read. Anything that stops reading or holds input is a configuration choice documented as a hazard, because the worker cannot see the graph it is placed in. Meet it: Prove it with a recorded live test where one source feeds both this component and a plain passthrough probe, and the probe keeps receiving.behavior.state(review) — stateless unless output depends on history. Persisted state holds only behavioral memory — values whose loss changes the next output. Model handles, clients and caches live per process. Declaring state serializes the vertex, so state that is not needed costs throughput. Meet it: Keep the component stateless, or justify each persisted field by the output it changes.behavior.side_effects(review, conditional) — outside calls behind virtual streams. HTTP, devices, SDK callbacks and other outside work run behind virtual streams so the graph-facing function stays visible to the scheduler. Retries are bounded and documented. Meet it: Move outside calls behind virtual input and output; bound and document retries. Mark not applicable when the component calls nothing outside the graph.behavior.live_params(review) — live-changeable only where safe. A parameter is mutable on a running deployment only when changing it mid-stream leaves every output correct. Meet it: Mark mutable only the parameters whose change the code applies safely between items.behavior.upstream_parity(live, conditional) — as good and as fast as the code it wraps. A component wrapping an upstream model or library matches or beats the upstream code's output quality and speed on the same hardware. A wrapper that is worse than the code it wraps has no reason to exist. The reference is the upstream inference path for the declared task, every branch it takes on input properties and its preprocessing, decoding, suppression and thresholds included; steps that serve one upstream demo or deployment scene beyond the component's declared task (scene heuristics, filters fixed on classes the task does not name) are not part of it and are not carried into the component. Meet it: Measure output and latency against the upstream inference path on the same inputs and hardware in a recorded live test. Evidence: the upstream entry point used as the reference, the lease and the comparison. Mark not applicable when nothing upstream is wrapped.
Build
build.pinned(auto) — dependencies pinned exactly. Every dependency the manifest declares — src/requirements.txt for Python, xmake_packages for C++ — is pinned to one exact version, and packages the build system already provides are not restated, so a rebuild resolves the same packages. A system package the build image provides (pkgconfig::,cmake::,system::,apt::) takes its version from the image and names none. Meet it: Pin each requirement aspkg==X.Y.Zand each xmake package asname X.Y.Z(conan::name/X.Y.Z); write a system package as its bare name (pkgconfig::name); pick the build_system that carries the runtime's own packages.build.license(auto) — license declared. The component declares the license its own code ships under, so whoever adds it to a backend knows what they may do with it. A model or other file bound through a file slot carries its own license on that file; the component license never covers or restates it. Meet it: Set the component license (SPDX id or text) to the license of the component's code alone; set each model file's license on the file.build.no_secrets(auto) — no secret in source. No credential appears in source, manifest or assets. Secrets reach the worker only as secret parameters bound to workspace secrets. Meet it: Remove the credential, revoke it, and take it through a secret parameter.
Doku
docs.true(review) — documents say exactly what the code does. The README, the AGENT manifest and every description in component.yml are exactly true of the code. A document that oversells or drifts is a bug as severe as wrong output, because agents quote it to justify wiring. Meet it: Read each statement against the source. Evidence: the statements checked and the code each rests on.docs.two_audiences(auto) — a README for people and an AGENT manifest for agents. The README explains how the component fits into pipelines and its caveats; the AGENT manifest says when to pick it, when not to, and how it behaves. Both pass the document rules. Meet it: Write both documents;ppl component rules --surface=readmeand--surface=agentlist what each must hold.docs.hazards(review) — every hazard documented. Every way the component can stall a graph, drop data, cost money or fail is a caveat or behavior note, not only its happy path. Meet it: List the failure, stall, cost and limit cases from the code and document each. Evidence: the hazards found and where each is documented.docs.refs(auto) — declared names referenced by role. Every role marker in the README resolves to what the manifest declares, and parameters are named through :param: markers rather than as bare words, so a rename cannot leave a stale name behind. Meet it: Use :param:, :type:, :input:, :output: and :component: markers for declared names; fix or drop each marker that resolves to nothing.
Erscheinung
look.name(review) — named by what it does. The display name starts with an imperative verb and its object, with the engine in parentheses — "Build Vector Index (FAISS)", not "Vector Store (HNSW)" — so the catalog reads as a list of capabilities. The engine is the library or runtime the code runs, never a model or checkpoint bound through a file slot. Meet it: Rename the component to verb + object (Engine), with no model name in it.look.icon(auto) — its own icon. The component carries its own icon, never the placeholder, so it is recognizable in every catalog row. Meet it: Draw a 512×512 icon — one silhouette, two or three flat colors, no text — and set it as the component avatar.look.tags(auto) — category and modality tags. The component carries at least one category and one modality, the filters the catalog is browsed by. Meet it: Set the component's categories and modalities.
Nachweis
proof.tests(live) — self-contained tests pass against the version. The component's own tests cover empty, degenerate, malformed, boundary, large and real inputs and every output arm, asserting real values, and pass against the published version. Meet it: Ship the tests with the component and run them as the live test's runner. Proven by a recorded live test whose runners all exited 0.proof.live(live) — output compared on chosen input. The published version ran live on input whose content was chosen for it, and its output content was compared with what that input should become. A ready deployment or a returned file id shows the graph ran, not what it produced. Meet it: Run a live test on a fixture you chose and compare the collected output. Evidence: the lease and the comparison.
Wo das hinpasst
Components sind das tragende Primitive der Plattform für was Code tut. Backends komponieren sie; deployments führen sie aus; der Katalog entdeckt sie; das Typsystem schränkt ein, wie sie zusammen verdrahtet werden; das release-Modell hält ihr Verhalten über die Zeit reproduzierbar. Jedes andere Konzept auf der Plattform existiert, um components wiederverwendbar, komponierbar, betreibbar oder auffindbar zu machen, sodass eine einzelne Fähigkeit viele Produkte bedient, statt als maßgeschneiderter Glue-Code für jedes neu implementiert zu werden.
Die Disziplin, die die Plattform von component-Autoren verlangt, ist klein: deklariere den typisierten Vertrag genau, defaulte auf stateless, greife nur dann nach state und virtual streams, wenn die Grundform den Vertrag nicht ausdrücken kann, und behandle releases als unveränderlich. Im Gegenzug bietet die Plattform Graph-Typ-Prüfung, Katalog-Discovery, runtime-Scheduling, deployment, Observability und Beweisschleifen. Der Autor schreibt die Funktion; alles darum herum ist die Verantwortung der Plattform.
Verwandt
- Backends — released components in einen typisierten Graphen verdrahten.
- Types — der Vertrag, der schlechte Verdrahtung vor dem runtime fängt.
- Named types — die Registry hinter
Image,Tensor,AudioFrame. - Publish-Semantik — die publish- und promote-Schleife im Detail.
- File schema — die files deklarieren, die ein component konsumiert und produziert.
- Build systems — wie die Plattform Quellcode in ein validiertes Image verwandelt.
- Solutions — component + backend + deployment + surface, End-to-End.
- Quickstart — der Routen-Wähler für deine erste Aufgabe.