Architektur

Auf dieser Seite

Der Hub ist ein einziges Rust-Programm, das einem Modell zwei Werkzeuge gibt, execute_script und get_state, und jede Anwendung über ein Modul auf der Seite des Hubs und eine Brücke auf der Seite der Anwendung erreicht. Die Seite zeigt, wie die Teile zusammenhängen, die drei Wege zu einer Anwendung und ein Skript im Vergleich mit getrennten Aufrufen.

Die Teile

  • MCP-Client: auf Ihrer Seite. Er startet libremodular-mcp serve und ruft die beiden Werkzeuge auf.
  • Hub: ein Prozess, libremodular-mcp. Er antwortet dem Client, prüft das Ziel, liest das Skript vor dem Senden und schreibt das Audit-Journal.
  • Modul: im Hub, eines pro Anwendung. Es weiß, wie die Anwendung zu erreichen ist.
  • Brücke: in der Anwendung, neben ihr oder als ein Python-Prozess pro Skript. Sie führt das Skript aus und gibt zurück, was es ausgegeben hat, sein result, oder den anwendungseigenen Fehler.
  • Anwendung: GIMP, Krita, Blender, Inkscape oder LibreOffice. Sie leistet die Arbeit.

Der Hub kennt die Schnittstelle keiner Anwendung. Ein Skript ist im Python der jeweiligen Anwendung geschrieben, und nur das Modul und die Brücke hängen von der Anwendung ab.

Der Hub ist eine einzelne ausführbare Datei von etwa 4,3 MB. Neben dem Lader listet ldd zwei Bibliotheken auf: libc.so.6 und libgcc_s.so.1. Das Paket installiert eine zweite ausführbare Datei, libremodular-launch, die LibreOffice und dessen Brücke startet.

Zwei Werkzeuge

Die Antwort des Hubs auf tools/list nennt genau ["execute_script", "get_state"].

execute_script(target_app, script_content) führt ein vollständiges Python-Skript in der Anwendung aus, die target_app nennt, und gibt ein JSON-Objekt zurück: status, duration_ms, stdout, stderr, result, error mit der Fehlerspur der Anwendung, truncated und reason, wenn der Hub das Skript abgelehnt hat. get_state(target_app) gibt den Zustand der Anwendung zurück: ihre Dokumente, Ebenen oder Objekte, einen Pfad zu einer PNG-Aufnahme, falls es eine gibt, und content_origin, das angibt, dass der Text aus einem Dokument stammt und nicht vom Hub. Das Ziel hub listet die konfigurierten Anwendungen auf, ohne eine davon zu kontaktieren.

Drei Wege zu einer Anwendung

Der Hub wurde mit jeder aktivierten Anwendung und ohne gestartete Brücke gestartet. get_state für hub listete sieben Ziele in diesen Modi auf:

  • tcp: gimp, krita, blender. Eine in die Anwendung geladene Brücke lauscht an einem lokalen Port, und der Hub verbindet sich mit ihr.
  • uno: libreoffice-writer, libreoffice-calc, libreoffice-impress. Eine eigenständige Brücke verbindet sich mit dem UNO-Port, an dem LibreOffice lauscht, und die drei Ziele teilen sie.
  • cli: inkscape. Der Hub startet pro Skript einen Python-Prozess; es gibt keine Brücke zu starten.

Nur inkscape zeigte connected: true: Im Modus cli muss nichts laufen, und der Hub hatte das Programm gefunden. In den Modi tcp und uno startet, schließt oder beendet der Hub nie eine Anwendung auf Wunsch des Modells; der Bediener startet die Brücke, wie es der Leitfaden jeder Anwendung beschreibt.

Eine Anwendung führt jeweils ein Skript aus. Ein Skript, das die Frist des Hubs überschreitet, erhält die Antwort timeout, und die nächste Anfrage erhält busy, bis die Anwendung wieder frei ist; die Seite Sicherheit misst das.

Was eine Brücke braucht

Die vier Brückendateien, die das Paket installiert, werden aus einem gemeinsamen Kern und einem Adapter pro Anwendung erzeugt. Sie importieren die Python-Standardbibliothek und darüber hinaus nur die eigene Schnittstelle der Anwendung. Jede Zeile nennt die Datei, ihre Länge und das, was sie außerhalb der Standardbibliothek importiert:

  • blender_bridge.py, 1 091 Zeilen: bpy.
  • gimp_bridge.py, 1 130 Zeilen: gi.
  • krita_bridge.py, 1 094 Zeilen: krita, PyQt5, PyQt6.
  • uno_bridge.py, 1 203 Zeilen: uno, com.

Die Liste stammt aus dem Parsen jeder Datei, nicht aus ihrer Ausführung. Die Seite Kompatibilität nennt für jede Anwendung die Version, mit der diese Brücken erprobt wurden.

Ein Skript gegen getrennte Aufrufe

Ein Modell, das eine Anwendung Arbeitsschritt für Arbeitsschritt steuert, macht pro Arbeitsschritt einen Aufruf. Ein Modell, das die ganze Arbeit schreibt, macht einen Aufruf. Der Hub behandelt beide gleich, daher lassen sich beide mit demselben Hub, derselben Anwendung und derselben Arbeit zeitlich vergleichen.

Die Arbeit besteht darin, einem Dokument N Elemente hinzuzufügen: N Rechtecke in Inkscape, N Würfel in Blender. Für jedes N maßen zehn Wiederholungen einen Aufruf von execute_script, der die ganze Arbeit erledigt, und dann N Aufrufe, die je einen Arbeitsschritt erledigen. Das Dokument wurde vor jeder Messung geleert, und zwei Aufwärmaufrufe gingen den Wiederholungen voraus. Die Zeit wird im Client gelesen, vom Schreiben der Anfrage bis zum Lesen der Antwort; in Inkscape ist die Arbeit dieses Skript,

python · Geschriebenes Skript
import json, os
import inkex
document = inkex.load_svg(os.environ['LMM_DOCUMENT'])
root = document.getroot()
for i in range(20):
    root.add(inkex.Rectangle.new(i * 12, 10, 10, 10))
document.write(os.environ['LMM_DOCUMENT'])
print('LMM_RESULT: ' + json.dumps({'count': len(root)}))

und jeder der zwanzig getrennten Aufrufe ist dasselbe Skript, wobei die Schleife durch eine Zeile ersetzt ist, zum Beispiel der dritte:

python · Geschriebenes Skript
import json, os
import inkex
document = inkex.load_svg(os.environ['LMM_DOCUMENT'])
root = document.getroot()
root.add(inkex.Rectangle.new(36, 10, 10, 10))
document.write(os.environ['LMM_DOCUMENT'])
print('LMM_RESULT: ' + json.dumps({'count': len(root)}))

Median aus zehn Wiederholungen, der kleinste und der größte Wert in Klammern. Der Client ist der der Demonstration, mit einer Uhr um jeden Aufruf.

Inkscape 1.4, Modus cli (ein Python-Prozess pro Aufruf):

  • N = 1: ein Skript 141,7 ms (141,7 bis 151,7); ein Aufruf 141,7 ms (141,5 bis 161,9); Faktor ×1,0.
  • N = 5: ein Skript 146,7 ms (141,6 bis 152,0); fünf Aufrufe 738,7 ms (718,6 bis 749,3); Faktor ×5,0.
  • N = 20: ein Skript 151,7 ms (141,6 bis 151,9); zwanzig Aufrufe 2 933,8 ms (2 874,0 bis 3 046,2); Faktor ×19,3.

Blender 4.3.2, Modus tcp (die Anwendung bleibt in Betrieb):

  • N = 1: ein Skript 1,5 ms (1,3 bis 2,7); ein Aufruf 1,2 ms (1,0 bis 2,1); Faktor ×0,8.
  • N = 5: ein Skript 3,5 ms (3,3 bis 6,1); fünf Aufrufe 5,9 ms (5,5 bis 8,3); Faktor ×1,7.
  • N = 20: ein Skript 17,8 ms (16,7 bis 20,4); zwanzig Aufrufe 30,5 ms (27,2 bis 32,3); Faktor ×1,7.

Im Modus cli startet jeder Aufruf einen Python-Prozess und lädt inkex, was unabhängig von der Arbeit etwa 0,14 s kostet: Zwanzig Arbeitsschritte in getrennten Aufrufen kosten das 19,3-Fache des einen Aufrufs. Im Modus tcp läuft die Anwendung bereits, und die 19 zusätzlichen Aufrufe fügten 12,7 ms hinzu, etwa 0,7 ms je Aufruf. Ein zweiter Lauf der gesamten Messung ergab Mediane, die sich bei Inkscape um höchstens 10 ms unterschieden, außer bei den zwanzig getrennten Aufrufen (87 ms), und bei Blender um höchstens 1,3 ms; der Faktor bei N = 20 war ×19,9 bei Inkscape und ×1,7 bei Blender.

WeiterSicherheit →