Discourse AI вызывает новые ошибки SSL и Connection Reset by Peer

Приоритет/Серьезность:
Недавние изменения в репозитории делают Discourse AI в основном неработоспособным с текущим API OpenAI.

Платформа:

  • Самохостинг, стандартная автономная сборка
  • Хост-ВМ Ubuntu 24.04, контейнеры Docker
  • API OpenAI
  • API Anthropic

Описание:

Discourse AI осуществлял вызовы внешнего API — OpenAI с моделями, и все работало хорошо по состоянию на 15 февраля (последняя пересборка контейнера). Сегодня (21 февраля) я пересобрал контейнер, и теперь все не работает.

Вот что мне известно:

По состоянию на 15 февраля
Модели OpenAI настроены и работают корректно:

  • LLM/Персона
    • GPT4 Omni
    • GPT4 Omni Mini
  • Векторные представления (Embeddings)
    • text-embedding-ada-002

По состоянию на 21 февраля

Все модели OpenAI имеют уровень ошибок около 70–80% при вызовах LLM с сообщением об ошибке «Connection Reset by Peer». Некоторые чаты проходят успешно, другие прерываются на полпути. Вызовы для векторных представлений (Embeddings) завершаются ошибкой SSL Faraday::ConnectionFailed.

Дополнительные модели OpenAI не работают:

  • o1-mini и o1-preview не могут быть протестированы/сохранены как LLM из-за ошибки кода («developer» не является допустимой ролью), поскольку роль developer действительна только для моделей o1 и o3, но не для их версий -mini. Исходный код github.com/discourse/discourse-ai/…/chat_gpt.rb:61 требует обновления: необходимо использовать точное совпадение имени модели вместо проверки starts_with. В случае else на строке 73 больше нет пользователя system, и его следует заменить на просто user. По состоянию на сегодня o1-mini не поддерживает использование инструментов.

Пробовал следующее:

  • Проверил лимиты платформы OpenAI — мы значительно ниже лимитов частоты запросов, аккаунт OpenAI пополнен.
  • Пересобрал контейнер
  • Удалил и воссоздал персоны и пользователей LLM
  • Удалил и создал модели LLM заново
  • Создал новые ключи API-токенов
  • Убедился, что SSL и сертификаты обновлены внутри контейнера
  • Зашел в контейнер и вызвал API через bash и curl (успешно)
  • Зашел в консоль Rails RAILS_ENV=production bundle exec rails console и вызвал API OpenAI через объект http (успешно)
  • Вызовы API Anthropic к claude-3.5-sonnet (успешно)

Шаги для воспроизведения:

Создайте новую сборку контейнера с последней версией Discourse и добавьте плагин Discourse AI в раздел плагинов:

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

Настройте модели LLM и векторных представлений (Embeddings) OpenAI со следующими параметрами:

  • GPT4 Omni, GPT4 Omni Mini
    • Все значения по умолчанию, введите свой API-ключ
    • Токены: 64000
    • Нажмите «Run test», дождитесь ответа — иногда успешно, часто «Internal Server Error». При успешном тестировании попытка чата с персоной приводит к стеку трассировки Inference LLM Model.
  • text-embedding-ada-002, text-embedding-3-large
    • Сохраняются успешно, но генерируют записи об ошибках в логах, повторяющиеся каждые 5 минут

Стек трассировки ошибки «Internal Server Error»

Стек трассировки ошибки Internal Server Error
Сообщение (2 сообщения)
Errno::ECONNRESET (Connection reset by peer)
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/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:26: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'
Backtrace
openssl (3.3.0) lib/openssl/buffering.rb:217:in `sysread_nonblock'
openssl (3.3.0) lib/openssl/buffering.rb:217:in `read_nonblock'
net-protocol (0.2.2) lib/net/protocol.rb:218:in `rbuf_fill'
net-protocol (0.2.2) lib/net/protocol.rb:199:in `readuntil'
net-protocol (0.2.2) lib/net/protocol.rb:209:in `readline'
net-http (0.6.0) lib/net/http/response.rb:625:in `read_chunked'
net-http (0.6.0) lib/net/http/response.rb:595:in `block in read_body_0'
net-http (0.6.0) lib/net/http/response.rb:570:in `inflater'
net-http (0.6.0) lib/net/http/response.rb:593:in `read_body_0'
net-http (0.6.0) lib/net/http/response.rb:363:in `read_body'
plugins/discourse-ai/lib/completions/endpoints/base.rb:374:in `non_streaming_response'
plugins/discourse-ai/lib/completions/endpoints/base.rb:160:in `block (2 levels) in perform_completion!'
net-http (0.6.0) lib/net/http.rb:2433:in `block in transport_request'
net-http (0.6.0) lib/net/http/response.rb:320:in `reading_body'
net-http (0.6.0) lib/net/http.rb:2430:in `transport_request'
net-http (0.6.0) lib/net/http.rb:2384:in `request'
rack-mini-profiler (3.3.1) lib/patches/net_patches.rb:19:in `block in request_with_mini_profiler' 
rack-mini-profiler (3.3.1) lib/mini_profiler/profiling_methods.rb:44:in `step' 
rack-mini-profiler (3.3.1) lib/patches/net_patches.rb:18:in `request_with_mini_profiler' 
(eval at /var/www/discourse/lib/method_profiler.rb:38):12:in `request'
plugins/discourse-ai/lib/completions/endpoints/base.rb:122:in `block in perform_completion!'
net-http (0.6.0) lib/net/http.rb:1632: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:26: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'
plugins/discourse-prometheus/lib/middleware/metrics.rb:14: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>'

В логах наблюдаются следующие ошибки:

Модель векторных представлений (Embeddings)

Сообщение об ошибке в логах: (каждые 5 минут) Connection reset by peer (Faraday::ConnectionFailed)

Версия приложения: 00907363d4b290df1c755df1a2494b95265e40b4

Задача: Jobs::EmbeddingsBackfill

Стек трассировки ошибки модели векторных представлений

Стек трассировки ошибки модели векторных представлений
Исключение задачи: 5 ошибок
Connection reset by peer (Faraday::ConnectionFailed)
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/openssl-3.3.0/lib/openssl/buffering.rb:217:in `sysread_nonblock'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/openssl-3.3.0/lib/openssl/buffering.rb:217:in `read_nonblock'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-protocol-0.2.2/lib/net/protocol.rb:218:in `rbuf_fill'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-protocol-0.2.2/lib/net/protocol.rb:199:in `readuntil'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-protocol-0.2.2/lib/net/protocol.rb:209:in `readline'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-http-0.6.0/lib/net/http/response.rb:625:in `read_chunked'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-http-0.6.0/lib/net/http/response.rb:595:in `block in read_body_0'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-http-0.6.0/lib/net/http/response.rb:570:in `inflater'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-http-0.6.0/lib/net/http/response.rb:593:in `read_body_0'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-http-0.6.0/lib/net/http/response.rb:363:in `read_body'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-http-0.6.0/lib/net/http/response.rb:401:in `body'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-http-0.6.0/lib/net/http/response.rb:321:in `reading_body'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-http-0.6.0/lib/net/http.rb:2430:in `transport_request'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-http-0.6.0/lib/net/http.rb:2384:in `request'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rack-mini-profiler-3.3.1/lib/patches/net_patches.rb:19:in `block in request_with_mini_profiler'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rack-mini-profiler-3.3.1/lib/mini_profiler/profiling_methods.rb:50:in `step'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rack-mini-profiler-3.3.1/lib/patches/net_patches.rb:18:in `request_with_mini_profil...
Backtrace
concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promises.rb:1268:in `raise' 
concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promises.rb:1268:in `wait_until_resolved!' 
concurrent-ruby-1.3.5/lib/concurrent-ruby/concurrent/promises.rb:998:in `value!' 
/var/www/discourse/plugins/discourse-ai/lib/embeddings/vector.rb:50:in `gen_bulk_reprensentations' 
/var/www/discourse/plugins/discourse-ai/app/jobs/scheduled/embeddings_backfill.rb:134:in `block in populate_topic_embeddings' 
/var/www/discourse/plugins/discourse-ai/app/jobs/scheduled/embeddings_backfill.rb:133:in `each' 
/var/www/discourse/plugins/discourse-ai/app/jobs/scheduled/embeddings_backfill.rb:133:in `each_slice' 
/var/www/discourse/plugins/discourse-ai/app/jobs/scheduled/embeddings_backfill.rb:133:in `populate_topic_embeddings' 
/var/www/discourse/plugins/discourse-ai/app/jobs/scheduled/embeddings_backfill.rb:36:in `execute' 
/var/www/discourse/app/jobs/base.rb:316:in `block (2 levels) in perform' 
rails_multisite-6.1.0/lib/rails_multisite/connection_management/null_instance.rb:49:in `with_connection'
rails_multisite-6.1.0/lib/rails_multisite/connection_management.rb:21:in `with_connection'
/var/www/discourse/app/jobs/base.rb:303:in `block in perform' 
/var/www/discourse/app/jobs/base.rb:299:in `each' 
/var/www/discourse/app/jobs/base.rb:299:in `perform' 
/var/www/discourse/app/jobs/base.rb:379:in `perform' 
mini_scheduler-0.18.0/lib/mini_scheduler/manager.rb:137:in `process_queue' 
mini_scheduler-0.18.0/lib/mini_scheduler/manager.rb:77:in `worker_loop' 
mini_scheduler-0.18.0/lib/mini_scheduler/manager.rb:63:in `block (2 levels) in ensure_worker_threads' 

Модель LLM для инференса

Сообщение об ошибке в логах: Job exception: Connection reset by peer

Версия приложения: 00907363d4b290df1c755df1a2494b95265e40b4

Задача: Jobs::CreateAiReply

Стек трассировки ошибки модели LLM

Стек трассировки ошибки модели LLM
Сообщение
Job exception: Connection reset by peer
Backtrace
openssl-3.3.0/lib/openssl/buffering.rb:217:in `sysread_nonblock' 
openssl-3.3.0/lib/openssl/buffering.rb:217:in `read_nonblock' 
net-protocol-0.2.2/lib/net/protocol.rb:218:in `rbuf_fill' 
net-protocol-0.2.2/lib/net/protocol.rb:199:in `readuntil' 
net-protocol-0.2.2/lib/net/protocol.rb:209:in `readline' 
net-http-0.6.0/lib/net/http/response.rb:625:in `read_chunked' 
net-http-0.6.0/lib/net/http/response.rb:595:in `block in read_body_0' 
net-http-0.6.0/lib/net/http/response.rb:570:in `inflater' 
net-http-0.6.0/lib/net/http/response.rb:593:in `read_body_0' 
net-http-0.6.0/lib/net/http/response.rb:363:in `read_body' 
/var/www/discourse/plugins/discourse-ai/lib/completions/endpoints/base.rb:374:in `non_streaming_response' 
/var/www/discourse/plugins/discourse-ai/lib/completions/endpoints/base.rb:160:in `block (2 levels) in perform_completion!' 
net-http-0.6.0/lib/net/http.rb:2433:in `block in transport_request' 
net-http-0.6.0/lib/net/http/response.rb:320:in `reading_body' 
net-http-0.6.0/lib/net/http.rb:2430:in `transport_request' 
net-http-0.6.0/lib/net/http.rb:2384:in `request' 
rack-mini-profiler-3.3.1/lib/patches/net_patches.rb:19:in `block in request_with_mini_profiler' 
rack-mini-profiler-3.3.1/lib/mini_profiler/profiling_methods.rb:50:in `step' 
rack-mini-profiler-3.3.1/lib/patches/net_patches.rb:18:in `request_with_mini_profiler' 
(eval at /var/www/discourse/lib/method_profiler.rb:38):5:in `request'
/var/www/discourse/plugins/discourse-ai/lib/completions/endpoints/base.rb:122:in `block in perform_completion!' 
net-http-0.6.0/lib/net/http.rb:1632:in `start' 
net-http-0.6.0/lib/net/http.rb:1070:in `start' 
/var/www/discourse/plugins/discourse-ai/lib/completions/endpoints/base.rb:105:in `perform_completion!' 
/var/www/discourse/plugins/discourse-ai/lib/completions/endpoints/open_ai.rb:44:in `perform_completion!' 
/var/www/discourse/plugins/discourse-ai/lib/completions/llm.rb:281:in `generate' 
/var/www/discourse/plugins/discourse-ai/lib/ai_bot/bot.rb:65:in `get_updated_title' 
/var/www/discourse/plugins/discourse-ai/lib/ai_bot/playground.rb:252:in `title_playground' 
/var/www/discourse/plugins/discourse-ai/lib/ai_bot/playground.rb:561:in `ensure in reply_to' 
/var/www/discourse/plugins/discourse-ai/lib/ai_bot/playground.rb:561:in `reply_to' 
/var/www/discourse/plugins/discourse-ai/app/jobs/regular/create_ai_reply.rb:18:in `execute' 
/var/www/discourse/app/jobs/base.rb:316:in `block (2 levels) in perform' 
rails_multisite-6.1.0/lib/rails_multisite/connection_management/null_instance.rb:49:in `with_connection'
rails_multisite-6.1.0/lib/rails_multisite/connection_management.rb:21:in `with_connection'
/var/www/discourse/app/jobs/base.rb:303:in `block in perform' 
/var/www/discourse/app/jobs/base.rb:299:in `each' 
/var/www/discourse/app/jobs/base.rb:299:in `perform' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:202:in `execute_job' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:170:in `block (2 levels) in process' 
sidekiq-6.5.12/lib/sidekiq/middleware/chain.rb:177:in `block in invoke' 
/var/www/discourse/lib/sidekiq/pausable.rb:132:in `call' 
sidekiq-6.5.12/lib/sidekiq/middleware/chain.rb:179:in `block in invoke' 
sidekiq-6.5.12/lib/sidekiq/middleware/chain.rb:182:in `invoke' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:169:in `block in process' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:136:in `block (6 levels) in dispatch' 
sidekiq-6.5.12/lib/sidekiq/job_retry.rb:113:in `local' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:135:in `block (5 levels) in dispatch' 
sidekiq-6.5.12/lib/sidekiq.rb:44:in `block in <module:Sidekiq>' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:131:in `block (4 levels) in dispatch' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:263:in `stats' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:126:in `block (3 levels) in dispatch' 
sidekiq-6.5.12/lib/sidekiq/job_logger.rb:13:in `call' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:125:in `block (2 levels) in dispatch' 
sidekiq-6.5.12/lib/sidekiq/job_retry.rb:80:in `global' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:124:in `block in dispatch' 
sidekiq-6.5.12/lib/sidekiq/job_logger.rb:39:in `prepare' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:123:in `dispatch' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:168:in `process' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:78:in `process_one' 
sidekiq-6.5.12/lib/sidekiq/processor.rb:68:in `run' 
sidekiq-6.5.12/lib/sidekiq/component.rb:8:in `watchdog' 
sidekiq-6.5.12/lib/sidekiq/component.rb:17:in `block in safe_thread' 

Буду признателен за любые идеи, предложения и т.д.

1 лайк

За последний месяц мы выполнили более 2000 запросов к API OpenAI на этом сайте, и в наших логах нет подобных ошибок.

Я проверил другой наш сайт, который активно использует OpenAI: за последний месяц там было 80 000 запросов, и там зафиксировано ноль ошибок Faraday и только две ошибки «Connection Reset by Peer» за весь период.

Возможно, у вас проблемы с сетевым оборудованием сервера? У меня однажды возникла ошибка Errno::ECONNRESET (Connection reset by peer) из-за неисправного драйвера сетевого адаптера (NIC).

Разберёмся с этим.

1 лайк

Как вы и написали, изначально я думал, что это проблема сетевого стека, однако повторные вызовы API OpenAI из того же контейнера работают безупречно.

Это также относится к очень свежей сборке с коммитами от 21 февраля.

Чтобы доказать это (ценой сжигания токенов), я быстро написал скрипт для проверки сетевого стека OpenAI.

  • Работает 600 секунд (10 минут)
  • Выполняет один вызов завершения чата в секунду
  • Меняет промпт, чтобы избежать кэширования

Запустите внутри контейнера ./launcher enter app, сохраните приведенный ниже скрипт, сделайте его исполняемым с помощью chmod +x test_openai.sh, а затем выполните его командой OPENAI_API_KEY=.... ./test_openai.sh.

test_openai.sh
#!/bin/bash

# Длительность выполнения
DURATION_SECS=600

# Инициализация счетчиков
successful=0
unsuccessful=0
declare -A error_messages

# Функция для расчета процента
calc_percentage() {
    local total=$(($1 + $2))
    if [ $total -eq 0 ]; then
        echo "0.00"
    else
        echo "scale=2; ($2 * 100) / $total" | bc
    fi
}

# Функция для вывода статистики
print_stats() {
    local percent=$(calc_percentage $successful $unsuccessful)
    echo "-------------------"
    echo "Успешных вызовов: $successful"
    echo "Неудачных вызовов: $unsuccessful"
    echo "Коэффициент неудач: ${percent}%"
    echo "Сообщения об ошибках:"
    for error in "${!error_messages[@]}"; do
        echo "  - $error (${error_messages[$error]} раз(а))"
    done
}

end_time=$((SECONDS + DURATION_SECS))

counter=1
while [ $SECONDS -lt $end_time ]; do
    # Выполнение вызова API с таймаутом
    response=$(curl -s -w "\n%{http_code}" \
        -X POST \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer $OPENAI_API_KEY" \
        -d "{
            \"model\": \"gpt-4o-mini\",
            \"messages\": [{\"role\": \"user\", \"content\": \"Используйте это число, чтобы выбрать ответ из одного слова: $counter\"}]
        }" \
        --connect-timeout 5 \
        --max-time 10 \
        https://api.openai.com/v1/chat/completions 2>&1)

    # Получение последней строки (код состояния) и тела ответа
    http_code=$(echo "$response" | tail -n1)
    body=$(echo "$response" | sed '$d')

    # Проверка успешности вызова
    if [ "$http_code" = "200" ]; then
        ((successful++))
    else
        ((unsuccessful++))
        # Извлечение сообщения об ошибке
        error_msg=$(echo "$body" | grep -o '"message":"[^"]*"' | cut -d'"' -f4)
        if [ -z "$error_msg" ]; then
            error_msg="Ошибка подключения: $body"
        fi
        # Увеличение счетчика сообщений об ошибках
        ((error_messages["$error_msg"]++))
    fi

    # Вывод текущей статистики
    print_stats

    ((counter++))
    
    # Пауза в 1 секунду перед следующим вызовом
    sleep 1
done

В ходе тестирования моего скрипта коэффициент неудач составил менее 0,5%, что приемлемо при таком масштабе.

Это говорит о том, что проблема кроется в программном обеспечении Discourse, а не в контейнере или сетевом стеке, на которых оно работает.

Если это не будет исправлено в последнем коммите, я продолжу расследование.

2 лайка

Я исправил регрессию, связанную с o1-mini и o1-preview, здесь:

Однако я не понимаю, в чём проблема с SSL, ведь мы ничего не меняли в нашей базовой библиотеке.

Возможно, это связано с потоковой передачей. Попробуйте отключить потоковую передачу для вашей модели OpenAI LLM и посмотрите, решится ли проблема. В вашем тесте используется gpt-4o-mini без потоковой передачи.

3 лайка

Это здорово! Отличная работа!

В процессе диагностики я обнаружил ещё одну ошибку: на странице конфигурации LLM (/admin/plugins/discourse-ai/ai-llms/%/edit) при выборе любого из вариантов «Отключить поддержку нативных инструментов (использовать инструменты на основе XML) (опционально)» или «Отключить потоковые завершения (преобразовать потоковые запросы в непотоковые)» и нажатии кнопки «Сохранить» появляется временное уведомление «Успешно!», но после перезагрузки страницы оба или любой из выбранных вариантов оказываются снятыми.

Проблемы с сбросом соединения всё ещё сохраняются, и я продолжаю их исследовать. Похоже, это сочетание обработки сокетов в Ruby-коде (FinalDestination / разрешение DNS / Faraday) и контейнера Debian 12, запущенного на виртуальной машине Ubuntu 24.04.

Я развернул тестовую виртуальную машину Ubuntu 22.04, и там проблем нет: все эмбеддинги и инференс работают идеально. Сбросов соединения не наблюдалось.

Я продолжу работать над этой проблемой, возможно, она связана с новым способом, которым Ubuntu 24.04 управляет TCP-стеком через netplan.

2 лайка

Спасибо, сегодня решили проблему с персистентностью. Можете обновить и попробовать снова.

3 лайка

Небольшое обновление: нам не удалось настроить прямое подключение к API OpenAI в корпоративном диапазоне IP-адресов. Cloudflare отправлял пакеты RST примерно через 1 мс после установления TLS-соединения.

Поэтому мы развернули шлюз Cloudflare AI как прямую замену URL конечной точки API OpenAI, и он безупречно работает с конфигурацией LLM.

Похоже, что у Cloudflare есть недокументированная политика ограничения скорости для неизвестных диапазонов IP-адресов (то есть не Azure, AWS, GCP и т. д.), которая вступает в силу. Пул из 100 соединений для Embeddings превышал этот лимит.

Кстати, в Cloudflare есть функция аутентифицированного шлюза, которая добавляет специальный токен в заголовок.

Из их документации:

curl https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/openai/chat/completions \
  --header 'cf-aig-authorization: Bearer {CF_AIG_TOKEN}' \
  --header 'Authorization: Bearer OPENAI_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{"model": "gpt-4o" ........

Было бы здорово, если бы в экране конфигурации LLM появилась возможность добавлять заголовки для каждой LLM.

Тогда мы могли бы добавлять ключ и значение cf-aig-authorization для каждой LLM при каждом вызове.

Это сложная задача, ведь для такого редкого случая требуется много элементов интерфейса.

Есть ли шанс, что вы сможете попробовать openrouter.ai? Возможно, это тоже решит проблему.

Я не категорически против возможности добавления произвольных заголовков, но это очень продвинутая настройка. Возможно, было бы нормально, если бы она была скрыта за специальной настройкой сайта (настройка для включения расширенного интерфейса).

Может ли ваша компания помочь в разработке этого функционала для плагина с открытым исходным кодом?

1 лайк

Мне пока не удалось получить одобрение на вклад, но мы продолжим попытки. Спасибо за помощь на данный момент!