diff --git a/assets/js/src/alpine.js b/assets/js/src/alpine.js index 3868e9a09674..4c84a39e62f8 100644 --- a/assets/js/src/alpine.js +++ b/assets/js/src/alpine.js @@ -79,6 +79,36 @@ function loadGordonCards() { Alpine.magic('gordonCards', () => loadGordonCards) +// With the "browser" Gordon backend, the assistant runs in the reader's browser. Its loader +// script defines window.gordonDocs and is only added once a reader shows interest in Gordon, +// never on a plain page view. +let gordonAssistantLoading + +function loadGordonAssistant() { + gordonAssistantLoading ??= new Promise((resolve, reject) => { + const script = document.createElement('script') + script.src = window.GORDON_LOADER_URL + script.onload = () => { + if (window.gordonDocs) { + resolve(window.gordonDocs) + } else { + gordonAssistantLoading = undefined + reject(new Error('The Gordon loader did not define gordonDocs')) + } + } + script.onerror = () => { + // A later call (such as opening the panel again) can try again. + gordonAssistantLoading = undefined + script.remove() + reject(new Error('Failed to load the Gordon loader')) + } + document.head.append(script) + }) + return gordonAssistantLoading +} + +Alpine.magic('gordonAssistant', () => loadGordonAssistant) + // Swaps each ```openui-lang block for the card it describes. Until the card code has loaded, or // while a block is still streaming in and doesn't parse yet, it shows a placeholder. If the card // code can't load, or a finished block isn't a card, the block stays a plain code block. @@ -170,6 +200,14 @@ Alpine.store('gordon', { }, close() { this.isOpen = false + }, + // Downloads the in-browser assistant ahead of time, when a reader hovers over or focuses a + // Gordon button. + prefetch() { + if (window.GORDON_BACKEND !== 'browser') return + loadGordonAssistant() + .then((assistant) => assistant.prefetch()) + .catch((err) => console.error('Gordon:', err)) } }) diff --git a/hugo.yaml b/hugo.yaml index f5d9edafb2c7..b0249bb2a94a 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -171,6 +171,10 @@ params: # Lets Gordon answer with cards (steps, tables, notes, snippets, follow-up # questions) in production builds. Always on in other environments. cards: true + # Where Gordon gets its answers: "legacy" (the docs AI service) or + # "browser" (an assistant that runs in the reader's browser). Override + # with the HUGO_GORDON_BACKEND environment variable. + backend: legacy # === Example versions === # The following parameters are used in various ways in content and templates. diff --git a/layouts/_partials/gordon-chat.html b/layouts/_partials/gordon-chat.html index 01ecfd8819bf..b237a81ab9c3 100644 --- a/layouts/_partials/gordon-chat.html +++ b/layouts/_partials/gordon-chat.html @@ -13,6 +13,28 @@ window.GORDON_CARDS_SRC = '{{ $gordonCards.RelPermalink }}'; // Lets Gordon answer with cards. Always on outside production builds. window.GORDON_CARDS_ENABLED = {{ or (ne hugo.Environment "production") site.Params.gordon.cards }}; + {{- /* "legacy" streams answers from GORDON_BASE_URL. "browser" runs the assistant in the + reader's browser: the page loads its script from the loader URL, and the assistant sends + model requests to the proxy URL. */}} + {{- $backend := or (getenv "HUGO_GORDON_BACKEND") site.Params.gordon.backend "legacy" }} + {{- if not (in (slice "legacy" "browser") $backend) }} + {{- errorf "Unknown Gordon backend %q: use \"legacy\" or \"browser\"" $backend }} + {{- end }} + window.GORDON_BACKEND = {{ $backend }}; + {{- if eq $backend "browser" }} + {{- $proxyURL := "https://ai-backend-service-stage.docker.com/proxy" }} + {{- $loaderURL := "" }} + {{- if eq hugo.Environment "production" }} + {{- $proxyURL = "https://ai-backend-service.docker.com/proxy" }} + {{- else }} + {{- with getenv "HUGO_GORDON_PROXY_URL" }} + {{- $proxyURL = . }} + {{- end }} + {{- $loaderURL = getenv "HUGO_GORDON_LOADER_URL" }} + {{- end }} + window.GORDON_PROXY_URL = {{ $proxyURL }}; + window.GORDON_LOADER_URL = {{ or $loaderURL (printf "%s/docs-assistant/loader.js" $proxyURL) }}; + {{- end }}
class="prose prose-sm dark:prose-invert" > @@ -518,14 +661,20 @@

+ +

+
@@ -674,8 +831,21 @@

class="block min-h-[3rem] w-full resize-none rounded-lg border border-gray-300 bg-white p-3 leading-normal focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 focus:outline-none enabled:hover:border-white disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-600 dark:bg-gray-900 dark:text-white dark:focus:border-blue-400" >

+