toAiGateway() für ColdBox Core

Entwurfsvorschlag: ein gateway-förmiges Gegenstück zu ColdBoxs eigenem toAi().

On this page

Vorschlag: toAiGateway() — ein nativer ColdBox-Routing-DSL-Terminator für die bx-ai-Gateway-Webhook-Oberfläche

Status: Entwurf, verfasst von BX Agents aus (ortus-boxlang/bx-agents). Update seit dem ersten Entwurf: coldbox-platform (konkret ColdBox selbst, Router.cfc) WURDE später in derselben Session angehängt und direkt gelesen — die Cross-Owner-Grenze erwies sich als Session-Zustand, nicht als dauerhaft; sobald das Zip von ColdBox/coldbox-platform von seiner echten Download-URL geholt und entpackt wurde, wurde dessen Quelltext system/web/routing/Router.cfc vollständig gelesen. Das löste die beiden unten als "noch zu bestätigen" gelisteten Punkte, und korrigierte, wichtiger noch, einen echten Fehler, den der toAi()-/IAiRunnable-Abschnitt dieses Vorschlags aus einem früheren, nur-dokumentationsbasierten Durchgang geerbt hatte (siehe die Korrekturhinweise inline).

Warum

ColdBox 8.1 liefert zwei KI-spezifische Routing-DSL-Terminatoren aus:

  • route(pattern).toAi(target) — 4 automatisch registrierte Routen (invoke/stream/batch/info) gegen ein IAiRunnable-Ziel.
  • route(pattern).toMCP(target) — 1 Route, dispatcht an MCPRequestProcessor.

bx-ai liefert außerdem eine dritte HTTP-Oberfläche aus, für die es überhaupt keinen ColdBox-Terminator gibt: die IGateway-/aiGatewayRegistry()-Channel-Adapter-Webhook-Oberfläche (Slack-/Webhook-Zustellung, Human-in-the-Loop-Genehmigung), vorgelagert durch einen festen 3-Routen-Prozessor (bxModules.bxai.models.gateway.http.GatewayRequestProcessor::processHttp()). Heute bedeutet sie aus einer ColdBox-App heraus zu nutzen, 3 einfache Routen von Hand an einen Passthrough-Handler zu verdrahten. Das ist genau die Art von Verdrahtung, die toAi()/toMCP() bereits existieren, um Leuten zu ersparen, sie für die anderen beiden bx-ai-Oberflächen von Hand zu erledigen — dieser Vorschlag schließt die Lücke mit einem dritten Terminator, toAiGateway(), auf dieselbe Weise gebaut.

BX Agents (ein konventionsbasiertes Agenten-Framework-Modul über bx-ai + ColdBox) liefert diese Verdrahtung in der Zwischenzeit selbst aus — siehe "Aktuelle Umgehung" unten — genau damit sie gelöscht werden kann, sobald dies in Core landet.

Was bereits belegt ist (in dieser Session gegen den bx-ai-Quellcode verifiziert)

bxModules.bxai.models.gateway.http.GatewayRequestProcessor:

static string function processHttp() {
    var requestData = static.httpTransport.readRequest();
    var response     = route( requestData );
    static.httpTransport.writeResponse( response );
    return response.content;
}
  • Null Argumente, statisch. Er liest das lebende HTTP-Request selbst (ĂĽber cgi.PATH_INFO, cgi.REQUEST_METHOD, getHTTPRequestData()) und schreibt die Antwort selbst (ĂĽber bx:header/bx:content reset=true). Er braucht ColdBoxs event/rc/prc fĂĽr die eigene Logik nicht — und kann sie nicht nutzen.
  • Routet intern anhand von cgi.PATH_INFO, erwartet genau 3 Formen:
    • POST /gateways/{gatewayName}/events — eingehendes Plattform-Event
    • GET /interactions/{requestID} — eine ausstehende Genehmigungsinteraktion abfragen
    • POST /interactions/{requestID}/decisions — die Entscheidung eines Menschen einreichen
    • (plus OPTIONS-CORS-Preflight, ebenfalls intern behandelt)
  • Da er Pfadsegmente selbst parst, muss, was auch immer davorsitzt, diese 3 Formen wörtlich exponieren (kein zusätzliches Pfad-Präfix), damit die Segmentanzahl-/NamensprĂĽfungen in GatewayRequestProcessor.route() passen.
  • aiGatewayRegistry() löst Gateways nach Namen auf; am Routing selbst hängt nichts vom Inhalt der Registry ab, nur, dass Gateways irgendwann registriert wurden, bevor ein Request eintrifft (typischerweise beim App-Start).

Das bedeutet, toAiGateway() braucht überhaupt keine Adapter-Schnittstelle — anders als toAi()s IAiRunnable gibt es nichts, was eine Zielklasse implementieren müsste. Die gesamte Aufgabe des Terminators besteht darin, die richtigen Routen auf den richtigen statischen Aufruf zu registrieren und ColdBox zu sagen, danach nichts zu rendern (der Prozessor hat die echte Antwort bereits geschrieben).

Vorgeschlagene Core-Implementierung

Ein einzelner Terminator, der 3 Routen automatisch registriert (spiegelt toAi()s "ein Aufruf → N Routen"-Form) mit keinem Ziel-Argument (spiegelt toMCP()s zielloser Form, da das Routing selbst namensgetrieben aus der URL kommt, nicht aus einer WireBox-Zuordnung):

route( "/bxai" ).toAiGateway();

registriert, relativ zu wo auch immer route()s Muster verankert:

VerbPfadVerhalten
POST{pattern}/gateways/:gatewayName/eventseingehendes Plattform-Event
GET{pattern}/interactions/:requestIDInteraktion abfragen
POST{pattern}/interactions/:requestID/decisionsmenschliche Entscheidung einreichen

Alle 3 dispatchen an dieselbe generierte/interne Action, die nichts tut auĂźer:

function process( event, rc, prc ) {
    bxModules.bxai.models.gateway.http.GatewayRequestProcessor::processHttp();
    return event.noRender();
}

Offene Frage für wer auch immer das gegen echten Route.bx-Quellcode implementiert: ob ein Präfix {pattern} sicher ist, gegeben, dass GatewayRequestProcessor cgi.PATH_INFO unter der Annahme kein Präfix parst (siehe "wörtlich"-Hinweis oben). Zwei Wege, das aufzulösen, in Reihenfolge der Präferenz:

  1. toAiGateway() verankert immer an der App-Wurzel (ignoriert/lehnt ein nicht-leeres Muster ab), da die eigene Pfad-Parsing-Logik des Prozessors ohnehin kein Präfix vertragen kann.
  2. Falls ColdBoxs URL-Rewriting cgi.PATH_INFO immer den vollständigen angefragten Pfad widerspiegeln lässt (typisch für rewrite-alles-auf-index.bxm-ColdBox-Deployments), würde ein Präfix transparent "einfach funktionieren", und das wäre gar keine Einschränkung — empirisch prüfen, bevor eine Option gewählt wird.

Standard-Routen-Modifikatoren (.as(), .withModule(), .withDomain() usw.) sollten auf dieselbe Weise gelten wie bei toAi()/toMCP().

Aktuelle Umgehung (BX Agents, zu löschen, sobald dies landet)

BX Agents' Build-Pipeline generiert die entsprechende Verdrahtung heute von Hand:

  • RouterGenerator.bx erzeugt, nur wenn mindestens ein Channel-Adapter-Gateway vom Typ http konfiguriert ist:
    post( "/gateways/:gatewayName/events" ).toHandler( "Gateway.process" )
    get( "/interactions/:requestID" ).toHandler( "Gateway.process" )
    post( "/interactions/:requestID/decisions" ).toHandler( "Gateway.process" )
    
  • GatewayGenerator.bx erzeugt ein generiertes handlers/Gateway.bx mit genau einer Action:
    function process( event, rc, prc ) {
        bxModules.bxai.models.gateway.http.GatewayRequestProcessor::processHttp()
        return arguments.event.noRender()
    }
    
  • aiGatewayRegistry().register( aiGateway( type, options ) )-Aufrufe werden in Application.bx onApplicationStart() der generierten App eingefĂĽgt, einmal pro konfiguriertem Channel-Adapter-Gateway.

Sobald toAiGateway() in Core existiert, tauscht RouterGenerator seine 3 handgeschriebenen Routen gegen einen route( ... ).toAiGateway()-Aufruf, und GatewayGenerator hört auf, handlers/Gateway.bx überhaupt zu generieren — reine Löschung, keine neue BX-Agents-seitige Logik nötig.

Testplan fĂĽr den Core-PR

  • Unit: route(...).toAiGateway() registriert genau 3 Routen, korrekte Verben/Pfade, Standard-Routen-Modifikatoren gelten.
  • Integration: ein Live-Request an jeden der 3 Pfade erreicht GatewayRequestProcessor::processHttp() und liefert dessen Antwort wörtlich zurĂĽck (Statuscode, Header, Body) — ein Gateway vom Typ mock ĂĽber aiGatewayRegistry() im Test-Harness registrieren (kein echtes Netzwerk/LLM-Aufruf nötig, bx-ai liefert einen literalen "mock"- Provider genau dafĂĽr aus).
  • Regression: bestätigen, dass event.noRender() verhindert, dass ColdBox eine Antwort doppelt schreibt, nachdem processHttp() bereits eine ĂĽber bx:content reset=true geflusht hat.

Später in dieser Session bestätigt (Update)

ColdBox/coldbox-platform (8.1.0) wurde direkt geholt (https://downloads.ortussolutions.com/ortussolutions/coldbox/8.1.0/coldbox-8.1.0.zip) und system/web/routing/Router.cfc vollständig gelesen. Beide hier ursprünglich als "noch zu bestätigen" gelisteten Punkte sind jetzt aufgelöst, und eine frühere Annahme in genau diesem Vorschlag war falsch und wurde korrigiert:

  1. Die Zielauflösung von toAi(target) — wie angenommen bestätigt. Router.cfc: var runnableInstance = isSimpleValue( capturedRunnable ) ? getInstance( capturedRunnable ) : capturedRunnable. Ein String wird über WireBox getInstance() aufgelöst; ein lebendes Objekt wird direkt genutzt.

  2. Der echte IAiRunnable-Vertrag — KORRIGIERT, nicht das, was dieser Vorschlag ursprünglich sagte. Der Abschnitt "Was bereits belegt ist" oben (unverändert, weiterhin korrekt für die Gateway- Oberfläche) wurde nur aus dem bx-ai-Quellcode geschrieben. Separat verließ sich BX Agents' eigene M8-Arbeit auf eine Beschreibung von toAi()s Ziel-Vertrag aus der veröffentlichten Dokumentation, die sich als falsch herausstellte: invoke/stream/batch/info sind die Unterrouten-Namen, keine Methodennamen, die toAi() am Ziel aufruft. Router.cfcs tatsächliche Closures rufen runnableInstance.run( input, params, options ) und runnableInstance.stream( onChunk, input, params, options ) auf — d. h. bx-ais eigene IAiRunnable-Schnittstelle (bxModules.bxai.models.runnables.IAiRunnable), die AiAgent bereits nativ über AiBaseRunnable implementiert. Es ist überhaupt keine Adapter-Unterklasse nötig — der Rückgabewert des bloßen aiAgent()-BIF erfüllt toAi() bereits. Der Generator von BX Agents wurde entsprechend korrigiert (kein GeneratedAgentRunnable.bx/exposeAgentAsRunnable mehr).

  3. WireBoxs .toProvider(closure) — in dieser Session nicht erneut geprüft (Router.cfc berührt keine WireBox-Binder-Syntax); bleibt eine Annahme im config/WireBox.bx-Generator von BX Agents. Geringes Risiko: .toProvider() ist etabliertes, verbreitet genutztes WireBox-DSL, nur eben nichts, was dieser spezifische Quellcode-Durchgang gerade berührt hat.

Edit this page Download Markdown Last updated Aug 21, 2026, 6:33:28 PM