mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
Discourse AI is configured through an LlmModel row rather than settings, so the addon upserts one via rails: provider open_ai, the gateway's chat-completions URL, the model the gateway serves, and this role's virtual key. It then binds that row to ai_default_llm_model and to the bot, because a model that exists but is bound to nothing serves no surface. llm_models.url is POSTed verbatim, so DISCOURSE_LITELLM_CHAT_ENDPOINT carries the full /v1/chat/completions path rather than a base URL. Every other consumer in this requirement takes a base and appends; getting this one wrong produces a 404 at prompt time, long after a green deploy. Three things had to be cleared before any of that could work. Discourse refuses to call a private address unless the host is in allowed_internal_hosts, and it fails as an opaque provider error rather than as a blocked request. The script appends the gateway host to that list and leaves existing entries alone, which matters because the OIDC bootstrap in config.yml.j2 appends the discovery host to the same pipe-separated setting. Both sides check membership before adding, so they compose instead of clobbering each other. The launcher-managed container sits outside the compose graph and does not join the gateway network on its own. handlers/stack_host.yml adds DISCOURSE_LITELLM_NETWORK to its network list and config.yml.j2 passes the matching --network flag, both only while litellm is enabled. The bootstrap aborted before the plugin could install at all. after_code cloned every enabled plugin unconditionally, but a bundled plugin already sits in $home/plugins, git exits non-zero on the clone, and the whole bootstrap stops. discourse-ai is bundled in current images, so it now skips plugins whose source is bundled. mem_limit goes from 2g to 4g. `rails r` boots a second Rails inside the container already running unicorn and sidekiq, and at 2g the kernel kills it mid-run: measured in deploy-036-11 and again in deploy-ai-v2-7, where the task died with "Killed" and the deploy ended at exit 137. Upstream documents 4 GB as Discourse's own minimum, so this corrects an under-provisioned limit rather than working around the task. The variant resource budget still passes. Verified: the OOM with the exact task and exit code across two independent runs, the bundled-plugin abort, and that the budget test holds at 4g. Not verified: the registration has never completed, because every attempt so far died at the OOM first. The eight-fact read-back assertion and addons/discourse-ai.spec.js are both entirely unexercised, the spec because the addon bridge gate had it skipping. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
47 lines
2.3 KiB
YAML
47 lines
2.3 KiB
YAML
---
|
|
- name: "🔐 Verify the gateway contract for {{ application_id }}"
|
|
ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/svc-ai-litellm/tasks/utils/consumer_contract.yml') }}"
|
|
|
|
- name: "🤖 Register the platform gateway as the Discourse AI model"
|
|
ansible.builtin.command:
|
|
argv:
|
|
- container
|
|
- exec
|
|
- "{{ DISCOURSE_CONTAINER }}" # nocheck: container-exec-resolver the launcher names the container itself, so the bare name resolves in both modes
|
|
- rails
|
|
- r
|
|
- "{{ lookup('template', 'ai_gateway/register.rb.j2') }}"
|
|
changed_when: false
|
|
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
|
|
|
|
- name: "🔎 Read the Discourse AI model back out of the running instance"
|
|
ansible.builtin.command:
|
|
argv:
|
|
- container
|
|
- exec
|
|
- "{{ DISCOURSE_CONTAINER }}" # nocheck: container-exec-resolver the launcher names the container itself, so the bare name resolves in both modes
|
|
- rails
|
|
- r
|
|
- "{{ lookup('template', 'ai_gateway/verify.rb.j2') }}"
|
|
register: discourse_ai_readback
|
|
changed_when: false
|
|
|
|
- name: "🛑 Refuse a Discourse AI surface that does not answer through the gateway"
|
|
ansible.builtin.assert:
|
|
that:
|
|
- "('ai_gateway_url=' ~ DISCOURSE_LITELLM_CHAT_ENDPOINT) in discourse_ai_readback.stdout_lines"
|
|
- "('ai_gateway_model=' ~ LITELLM_CHAT_MODEL) in discourse_ai_readback.stdout_lines"
|
|
- "'ai_gateway_provider=open_ai' in discourse_ai_readback.stdout_lines"
|
|
- "'ai_gateway_key_configured=true' in discourse_ai_readback.stdout_lines"
|
|
- "'ai_gateway_host_allowed=true' in discourse_ai_readback.stdout_lines"
|
|
- "'ai_gateway_default_bound=true' in discourse_ai_readback.stdout_lines"
|
|
- "'ai_gateway_bot_bound=true' in discourse_ai_readback.stdout_lines"
|
|
- "'ai_gateway_plugin_enabled=true' in discourse_ai_readback.stdout_lines"
|
|
fail_msg: >-
|
|
The Discourse AI model '{{ DISCOURSE_AI_LLM_DISPLAY_NAME }}' does not resolve to
|
|
{{ DISCOURSE_LITELLM_CHAT_ENDPOINT }} on model {{ LITELLM_CHAT_MODEL }} with a
|
|
non-empty virtual key, '{{ DISCOURSE_LITELLM_HOST }}' allowlisted in
|
|
allowed_internal_hosts and the row bound to ai_default_llm_model, so prompts
|
|
would either leave the deployment or hit a dead surface. Read back:
|
|
{{ discourse_ai_readback.stdout_lines }}
|