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 einIAiRunnable-Ziel.route(pattern).toMCP(target)— 1 Route, dispatcht anMCPRequestProcessor.
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 (überbx:header/bx:content reset=true). Er braucht ColdBoxsevent/rc/prcfü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-EventGET /interactions/{requestID}— eine ausstehende Genehmigungsinteraktion abfragenPOST /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:
| Verb | Pfad | Verhalten |
|---|---|---|
| POST | {pattern}/gateways/:gatewayName/events | eingehendes Plattform-Event |
| GET | {pattern}/interactions/:requestID | Interaktion abfragen |
| POST | {pattern}/interactions/:requestID/decisions | menschliche 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:
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.- Falls ColdBoxs URL-Rewriting
cgi.PATH_INFOimmer 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.bxerzeugt, nur wenn mindestens ein Channel-Adapter-Gateway vom Typhttpkonfiguriert ist:post( "/gateways/:gatewayName/events" ).toHandler( "Gateway.process" ) get( "/interactions/:requestID" ).toHandler( "Gateway.process" ) post( "/interactions/:requestID/decisions" ).toHandler( "Gateway.process" )GatewayGenerator.bxerzeugt ein generierteshandlers/Gateway.bxmit 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 inApplication.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 TypmocküberaiGatewayRegistry()im Test-Harness registrieren (kein echtes Netzwerk/LLM-Aufruf nötig,bx-ailiefert einen literalen"mock"- Provider genau dafür aus). - Regression: bestätigen, dass
event.noRender()verhindert, dass ColdBox eine Antwort doppelt schreibt, nachdemprocessHttp()bereits eine ĂĽberbx:content reset=truegeflusht 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:
-
Die Zielauflösung von
toAi(target)— wie angenommen bestätigt. Router.cfc:var runnableInstance = isSimpleValue( capturedRunnable ) ? getInstance( capturedRunnable ) : capturedRunnable. Ein String wird über WireBoxgetInstance()aufgelöst; ein lebendes Objekt wird direkt genutzt. -
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 vontoAi()s Ziel-Vertrag aus der veröffentlichten Dokumentation, die sich als falsch herausstellte:invoke/stream/batch/infosind die Unterrouten-Namen, keine Methodennamen, dietoAi()am Ziel aufruft. Router.cfcs tatsächliche Closures rufenrunnableInstance.run( input, params, options )undrunnableInstance.stream( onChunk, input, params, options )auf — d. h. bx-ais eigeneIAiRunnable-Schnittstelle (bxModules.bxai.models.runnables.IAiRunnable), dieAiAgentbereits nativ überAiBaseRunnableimplementiert. Es ist überhaupt keine Adapter-Unterklasse nötig — der Rückgabewert des bloßenaiAgent()-BIF erfüllttoAi()bereits. Der Generator von BX Agents wurde entsprechend korrigiert (keinGeneratedAgentRunnable.bx/exposeAgentAsRunnablemehr). -
WireBoxs
.toProvider(closure)— in dieser Session nicht erneut geprüft (Router.cfc berührt keine WireBox-Binder-Syntax); bleibt eine Annahme imconfig/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.