Haciendo que Discourse AI funcione con ollama localmente

¿Alguno ha logrado que esto funcione con ollama localmente?

También estoy intentando que esto funcione con ollama, parece que la API que utiliza el plugin Discourse AI no es compatible con Ollama y cualquier configuración que cambie no modifica la solicitud de la API.

Esto es lo que funciona con ollama:

curl http://192.168.1.2:11434/api/generate -d '{"model": "llama3.2", "prompt": "Why is the sky blue?"}'

Y arriba parece ser el único tipo de solicitud que intenta el plugin. por ejemplo:

curl http://192.168.1.2:11434/ \
                           -X POST \
                           -H 'Content-Type: application/json' \
                           -d '{"inputs":"<s>[INST] What is your favourite condiment? [/INST] Well, Im quite partial to a good squeeze of fresh lemon juice. It adds just the right amount of zesty flavour to whatever Im cooking up in the kitchen!</s> [INST] Do you have mayonnaise recipes? [/INST]","parameters":{"max_new_tokens":500, "temperature":0.5,"top_p": 0.9}}'

El chatbot de IA supuestamente soporta ollama, pero tampoco puedo obtener una respuesta.

Así que sí, ¡cualquiera que haya conseguido que esto funcione con ollama, por favor publique qué configuración utilizó!

Parece que hablé demasiado pronto, si no tengo ninguna configuración en el plugin Discourse AI y sigo las instrucciones de RAG en esta publicación, obtengo respuestas en el chatbot de IA de Ollama.

Funciona perfectamente con ollama, tenemos varias personas en el equipo usándola.

Genial. ¿Hay alguna guía que podamos seguir sobre cómo implementarlo?

Me interesan más la resumen y otras funciones que el chatbot.

ETA: Esta configuración no funciona:

(Aquí he redactado la dirección IP de mi host).

Lo que sucede es que obtengo un Error Interno del Servidor cuando hago clic en “Ejecutar prueba”.

En /logs, veo:

NameError (variable o método local no definido `tokenizer' para una instancia de DiscourseAi::Completions::Dialects::ChatGpt)
app/controllers/application_controller.rb:427:in `block in with_resolved_locale'
app/controllers/application_controller.rb:427:in `with_resolved_locale'
lib/middleware/omniauth_bypass_middleware.rb:35:in `call'
lib/middleware/csp_script_nonce_injector.rb:12:in `call'
lib/middleware/anonymous_cache.rb:409:in `call'
lib/middleware/csp_script_nonce_injector.rb:12:in `call'
config/initializers/008-rack-cors.rb:14:in `call'
config/initializers/100-quiet_logger.rb:20:in `call'
config/initializers/100-silence_logger.rb:29:in `call'
lib/middleware/enforce_hostname.rb:24:in `call'
lib/middleware/processing_request.rb:12:in `call'
lib/middleware/request_tracker.rb:385:in `call'

Esto sucede independientemente del tokenizador que seleccione. Estoy probando esto en 3.5.0beta1-dev (c1ee4e120e).

Establece la URL en http://localhost:11434/v1/chat/completions

La prueba sigue fallando con el error interno del servidor, el mismo mensaje de registro también: un NameError que dice que ‘tokenizer’ es una variable local no definida.

Intenta guardar el modelo tal como está, actualiza la página y luego regresa a través del botón de edición de LLM y ejecuta una nueva prueba.

Mismo resultado: lo he intentado varias veces, pero lo acabo de hacer de nuevo.

Lo instalé de la forma habitual: lo añadí a mi archivo app.yml y ejecuté ./launcher rebuild app.

La línea que añadí fue:

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - sudo -E -u discourse git clone https://github.com/discourse/discourse-ai.git

(He eliminado los otros plugins de la lista, pero he dejado los componentes yml para que puedas ver dónde está esto en el archivo yml).

Recuperaré mi configuración local de ollama mañana y reportaré aquí.

Hice:

ollama serve
ollama run phi4

# probar

curl http://localhost:11434/v1/chat/completions \
              -H "Content-Type: application/json" \
              -d '{
              "model": "phi4",
              "messages": [
                  {
                      "role": "system",
                      "content": "Eres un asistente útil."
                  },
                  {
                      "role": "user",
                      "content": "¿Cuánto es 1+1?"
                  }
              ]
          }' -s | jq .choices[].message.content

"La suma de 1 + 1 es 2.

En aritmética básica, cuando se suma una unidad a otra unidad simple, el número total se convierte en dos unidades. Esta operación forma parte de los principios fundamentales de la suma utilizados en matemáticas. ¿Hay algo más en lo que pueda ayudarte sobre matemáticas o cualquier otro tema?"

Lo probé en Helper

Gracias por probar esto.

He notado que tengo una opción adicional (Deshabilitar completaciones en streaming) en comparación con tu configuración. ¿Es posible que la selección del modelo esté causando el problema? (Tengo instalado deepseek-r1:32b).

curl http://localhost:11434/v1/chat/completions \
              -H "Content-Type: application/json" \
              -d '{
              "model": "deepseek-r1:32b",
              "messages": [
                  {
                      "role": "system",
                      "content": "Eres un asistente útil."
                  },
                  {
                      "role": "user",
                      "content": "¿Cuánto es 1+1?"
                  }
              ]
          }' -s | jq .choices[].message.content

"<think>\nBien, estoy tratando de averiguar cuánto es 1 más 1. Vale, entonces desglosemos esto paso a paso, aunque parezca simple.\n\nPrimero, sé que la suma consiste en combinar dos números para obtener una suma total. Así que, cuando decimos 1 + 1, esencialmente estamos juntando un objeto y otro para ver cuántos tenemos en total.\n\nDéjame visualizarlo: imagina que tengo una manzana en mi mano izquierda y otra en mi mano derecha. Si las pongo juntas, ¿cuántas manzanas tengo? Deberían ser dos, ¿verdad?\n\nOtra forma de pensarlo es usando una recta numérica. Empezando en el número 1, si avanzo un paso más, ¿dónde aterrizo? Eso es correcto, en el número 2.\n\nTambién puedo usar mis dedos para contar. Levanta un dedo en la mano izquierda y otro en la derecha. Ahora, cuéntalos todos: uno, dos. Eso me da un total de dos dedos, lo que significa que 1 + 1 = 2.\n\nQuizás debería verificar si hay otra perspectiva o método. Por ejemplo, en binario, el sistema numérico utilizado por las computadoras, 1 + 1 es igual a 10 porque se produce un acarreo después de llegar a 2 (que se representa como 10). Pero eso es un contexto diferente. En la aritmética estándar en base 10, que usamos a diario, 1 + 1 sigue siendo 2.\n\nTambién puedo considerar el concepto de conjuntos. Si tengo un conjunto con un elemento y otro conjunto con un elemento, al combinarlos obtendría un conjunto con dos elementos. Eso refuerza que 1 + 1 es igual a 2.\n\nPodría preguntarme si hay algún escenario donde 1 + 1 no sea igual a 2. Pero dentro del ámbito de la aritmética básica y las matemáticas tal como las aprendemos, la respuesta es consistentemente dos.\n\nPara asegurarme de que no me estoy perdiendo nada, quizás debería investigar algunas demostraciones o principios matemáticos que confirmen esto. Por ejemplo, en los axiomas de Peano, que forman la base de la aritmética, la idea de sumar 1 a 1 seguiría el axioma de que todo número natural tiene un sucesor, y 2 se define como el sucesor de 1.\n\nPor lo tanto, reuniendo todos estos pensamientos —visualizar objetos, usar una recta numérica, contar con los dedos, considerar conjuntos e incluso tocar los axiomas matemáticos— todo apunta a que 1 + 1 es igual a 2.\n</think>\n\nEl resultado de sumar 1 y 1 es 2. Esto se puede ver en la aritmética simple y de varias formas de visualizar el problema.\n\n**Respuesta:**  \n$1 + 1 = \\boxed{2}$"

Resultado similar, aunque con este modelo en particular obtengo las etiquetas <think>, por lo que quizás el modelo no sea adecuado para este uso.

Acabo de reconstruir (es decir, ./launcher rebuild app) mi instalación y me aseguré de que todo esté actualizado; en mi página de Actualizaciones aparece “todo actualizado”.

Seleccionando ya sea deepseek-r1:32b o phi4 (que también instalé y probé), sigo obteniendo la respuesta de error interno del servidor en el navegador.

Pero en mis registros, estoy viendo algo nuevo.

FinalDestination::SSRFDetector::DisallowedIpError (FinalDestination: todas las IPs resueltas fueron prohibidas) lib/final_destination/ssrf_detector.rb:105:in 'lookup_and_filter_ips' lib/final_destination/http'

lib/final_destination/ssrf_detector.rb:105:in `lookup_and_filter_ips'
lib/final_destination/http.rb:15:in `connect'
net-http (0.6.0) lib/net/http.rb:1642:in `do_start'
net-http (0.6.0) lib/net/http.rb:1631:in `start'
net-http (0.6.0) lib/net/http.rb:1070:in `start'
plugins/discourse-ai/lib/completions/endpoints/base.rb:105:in `perform_completion!'
plugins/discourse-ai/lib/completions/endpoints/open_ai.rb:44:in `perform_completion!'
plugins/discourse-ai/lib/completions/llm.rb:281:in `generate'
plugins/discourse-ai/lib/configuration/llm_validator.rb:36:in `run_test'
plugins/discourse-ai/app/controllers/discourse_ai/admin/ai_llms_controller.rb:128:in `test'
actionpack (7.2.2.1) lib/action_controller/metal/basic_implicit_render.rb:8:in `send_action'
actionpack (7.2.2.1) lib/abstract_controller/base.rb:226:in `process_action'
actionpack (7.2.2.1) lib/action_controller/metal/rendering.rb:193:in `process_action'
actionpack (7.2.2.1) lib/abstract_controller/callbacks.rb:261:in `block in process_action'
activesupport (7.2.2.1) lib/active_support/callbacks.rb:121:in `block in run_callbacks'
app/controllers/application_controller.rb:427:in `block in with_resolved_locale'
i18n (1.14.7) lib/i18n.rb:353:in `with_locale'
app/controllers/application_controller.rb:427:in `with_resolved_locale'
activesupport (7.2.2.1) lib/active_support/callbacks.rb:130:in `block in run_callbacks'
activesupport (7.2.2.1) lib/active_support/callbacks.rb:141:in `run_callbacks'
actionpack (7.2.2.1) lib/abstract_controller/callbacks.rb:260:in `process_action'
actionpack (7.2.2.1) lib/action_controller/metal/rescue.rb:27:in `process_action'
actionpack (7.2.2.1) lib/action_controller/metal/instrumentation.rb:77:in `block in process_action'
activesupport (7.2.2.1) lib/active_support/notifications.rb:210:in `block in instrument'
activesupport (7.2.2.1) lib/active_support/notifications/instrumenter.rb:58:in `instrument'
activesupport (7.2.2.1) lib/active_support/notifications.rb:210:in `instrument'
actionpack (7.2.2.1) lib/action_controller/metal/instrumentation.rb:76:in `process_action'
actionpack (7.2.2.1) lib/action_controller/metal/params_wrapper.rb:259:in `process_action'
activerecord (7.2.2.1) lib/active_record/railties/controller_runtime.rb:39:in `process_action'
actionpack (7.2.2.1) lib/abstract_controller/base.rb:163:in `process'
actionview (7.2.2.1) lib/action_view/rendering.rb:40:in `process'
rack-mini-profiler (3.3.1) lib/mini_profiler/profiling_methods.rb:115:in `block in profile_method' 
actionpack (7.2.2.1) lib/action_controller/metal.rb:252:in `dispatch'
actionpack (7.2.2.1) lib/action_controller/metal.rb:335:in `dispatch'
actionpack (7.2.2.1) lib/action_dispatch/routing/route_set.rb:67:in `dispatch'
actionpack (7.2.2.1) lib/action_dispatch/routing/route_set.rb:50:in `serve'
actionpack (7.2.2.1) lib/action_dispatch/routing/mapper.rb:32:in `block in <class:Constraints>'
actionpack (7.2.2.1) lib/action_dispatch/routing/mapper.rb:62:in `serve'
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:53:in `block in serve'
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:133:in `block in find_routes'
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:126:in `each'
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:126:in `find_routes'
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:34:in `serve'
actionpack (7.2.2.1) lib/action_dispatch/routing/route_set.rb:896:in `call'
lib/middleware/omniauth_bypass_middleware.rb:35:in `call'
rack (2.2.11) lib/rack/tempfile_reaper.rb:15:in `call'
rack (2.2.11) lib/rack/conditional_get.rb:27:in `call'
rack (2.2.11) lib/rack/head.rb:12:in `call'
actionpack (7.2.2.1) lib/action_dispatch/http/permissions_policy.rb:38:in `call'
lib/content_security_policy/middleware.rb:12:in `call'
lib/middleware/anonymous_cache.rb:409:in `call'
lib/middleware/csp_script_nonce_injector.rb:12:in `call'
config/initializers/008-rack-cors.rb:14:in `call'
rack (2.2.11) lib/rack/session/abstract/id.rb:266:in `context'
rack (2.2.11) lib/rack/session/abstract/id.rb:260:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/cookies.rb:704:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/callbacks.rb:31:in `block in call'
activesupport (7.2.2.1) lib/active_support/callbacks.rb:101:in `run_callbacks'
actionpack (7.2.2.1) lib/action_dispatch/middleware/callbacks.rb:30:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/debug_exceptions.rb:31:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/show_exceptions.rb:32:in `call'
logster (2.20.1) lib/logster/middleware/reporter.rb:40:in `call'
railties (7.2.2.1) lib/rails/rack/logger.rb:41:in `call_app'
railties (7.2.2.1) lib/rails/rack/logger.rb:29:in `call'
config/initializers/100-quiet_logger.rb:20:in `call'
config/initializers/100-silence_logger.rb:29:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/request_id.rb:33:in `call'
lib/middleware/enforce_hostname.rb:24:in `call'
rack (2.2.11) lib/rack/method_override.rb:24:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/executor.rb:16:in `call'
rack (2.2.11) lib/rack/sendfile.rb:110:in `call'
rack-mini-profiler (3.3.1) lib/mini_profiler.rb:334:in `call'
lib/middleware/processing_request.rb:12:in `call'
message_bus (4.3.9) lib/message_bus/rack/middleware.rb:60:in `call'
lib/middleware/request_tracker.rb:385:in `call'
actionpack (7.2.2.1) lib/action_dispatch/middleware/remote_ip.rb:96:in `call'
railties (7.2.2.1) lib/rails/engine.rb:535:in `call'
railties (7.2.2.1) lib/rails/railtie.rb:226:in `public_send'
railties (7.2.2.1) lib/rails/railtie.rb:226:in `method_missing'
rack (2.2.11) lib/rack/urlmap.rb:74:in `block in call'
rack (2.2.11) lib/rack/urlmap.rb:58:in `each'
rack (2.2.11) lib/rack/urlmap.rb:58:in `call'
unicorn (6.1.0) lib/unicorn/http_server.rb:634:in `process_client'
unicorn (6.1.0) lib/unicorn/http_server.rb:739:in `worker_loop'
unicorn (6.1.0) lib/unicorn/http_server.rb:547:in `spawn_missing_workers'
unicorn (6.1.0) lib/unicorn/http_server.rb:143:in `start'
unicorn (6.1.0) bin/unicorn:128:in `<top (required)>'
vendor/bundle/ruby/3.3.0/bin/unicorn:25:in `load'
vendor/bundle/ruby/3.3.0/bin/unicorn:25:in `<main>'

Esta instalación de Discourse es relativamente nueva y no he configurado ningún bloqueo de direcciones IP. Además, soy el único usuario. Estoy ejecutando ollama en un contenedor Docker, pero eso no debería ser un problema; uso open-webui (también desde otro contenedor Docker) con la instancia y funciona bien. Estoy usando la dirección IP local en lugar de “localhost”, pero “localhost” no se comporta de manera diferente.

Agradezco el tiempo que estás dedicando para ayudar a averiguar qué está pasando aquí.

ETA: Instalé jq en el contenedor Docker de Discourse y ejecuté el comando curl (sustituyendo mi dirección IP local por localhost), y sí se ejecutó desde allí, por lo que hay conectividad entre los contenedores y la URL es correcta).

Esa es nuestra protección SSRF predeterminada que se activa. Puedes agregar excepciones a ella a través de la variable de entorno DISCOURSE_ALLOWED_INTERNAL_HOSTS.

Se activa cuando Discourse intenta conectarse a una IP considerada interna.

Genial, eso lo solucionó. Lo hice a través de la configuración en lugar de la variable de entorno, pero la prueba pasó ahora. ¡Gracias de nuevo por toda la ayuda!