diff --git a/configure.ac b/configure.ac index 38bbedd2..47a84d08 100644 --- a/configure.ac +++ b/configure.ac @@ -103,8 +103,32 @@ else fi AC_SUBST([TUN_LIBS]) +# ==================== WHISPER (OPTIONAL) ==================== +AC_ARG_WITH([whisper], + AS_HELP_STRING([--with-whisper], [Enable Whisper speech-to-text (requires libwhisper)]), + [with_whisper=$withval], + [with_whisper=no]) + +if test "x$with_whisper" = "xyes"; then + AC_CHECK_HEADER([whisper.h], [ + AC_CHECK_LIB([whisper], [whisper_init_from_file_with_params], [ + WHISPER_LIBS="-lwhisper -lggml -lggml-base" + AC_DEFINE([HAVE_WHISPER], [1], [Define to enable Whisper speech-to-text]) + AC_MSG_NOTICE([Whisper enabled]) + ], [ + AC_MSG_WARN([libwhisper not found, disabling whisper]) + with_whisper=no + ]) + ], [ + AC_MSG_WARN([whisper.h not found, disabling whisper]) + with_whisper=no + ]) +fi +AM_CONDITIONAL([HAVE_WHISPER], [test "x$with_whisper" = "xyes"]) +AC_SUBST([WHISPER_LIBS]) + # Checks for header files, typedefs, functions... -AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h netinet/in.h stdlib.h string.h sys/ioctl.h sys/socket.h unistd.h]) +AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h netinet/in.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h unistd.h]) AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_UINT16_T diff --git a/src/Makefile.am b/src/Makefile.am index e2264834..9170e490 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -181,8 +181,6 @@ utun_CFLAGS = \ -DSQLITE_THREADSAFE=1 \ $(DEBUG_FLAGS) -utun_chat_whisper_CFLAGS = $(utun_CFLAGS) -DHAVE_WHISPER - # Libraries utun_LDADD = \ libutun.a \ @@ -191,9 +189,13 @@ utun_LDADD = \ -lpthread \ -lm \ -lcrypto \ - -lwhisper -lggml -lggml-base \ $(utun_TUN_LIBS) +if HAVE_WHISPER +utun_CFLAGS += -DHAVE_WHISPER +utun_LDADD += @WHISPER_LIBS@ +endif + # Copy binary to project root after building all-local: copy-to-root diff --git a/src/chat/chat_whisper.c b/src/chat/chat_whisper.c index 1ba387be..63084ddc 100644 --- a/src/chat/chat_whisper.c +++ b/src/chat/chat_whisper.c @@ -31,8 +31,10 @@ /* ─── состояние ─── */ -static struct whisper_context* g_whisper_ctx = NULL; -static int g_initialized = 0; +#ifdef HAVE_WHISPER +static struct whisper_context *g_whisper_ctx = NULL; +#endif +static int g_initialized = 0; /* очередь транскрипций: whisper не thread-safe, обрабатываем по одной */ struct wh_job { @@ -234,6 +236,12 @@ static void chat_whisper_transcribe_async_impl( uint64_t reply_to_ts, uint64_t reply_to_node, chat_whisper_done_fn done_cb, void* done_arg); +static void chat_whisper_transcribe_async( + struct media_async* ma, struct UASYNC* ua, + const char* audio_path, const char* channel_id, + uint64_t reply_to_ts, uint64_t reply_to_node, + chat_whisper_done_fn done_cb, void* done_arg); + struct wh_work_ctx { struct wh_job job; char* text; /* результат транскрипции (u_malloc) */ diff --git a/tools/chatgui/transport/node_config.cpp b/tools/chatgui/transport/node_config.cpp index b9e05905..0ed47db6 100644 --- a/tools/chatgui/transport/node_config.cpp +++ b/tools/chatgui/transport/node_config.cpp @@ -305,6 +305,9 @@ bool NodeConfig::load() { else if (key == "debug_file") m_debugFile = val; else if (key == "debug_level") m_debugLevel = val; else if (key == "debug_categories") m_debugCategories = val; + } else if (debugSection) { + if (!m_debugCategories.isEmpty()) m_debugCategories += ','; + m_debugCategories += key + '=' + val; } } f.close(); @@ -354,6 +357,16 @@ bool NodeConfig::saveFull() { out << "debug_level=" << (m_debugLevel.isEmpty() ? "info" : m_debugLevel) << "\n"; out << "debug_categories=" << m_debugCategories << "\n\n"; + if (!m_debugCategories.isEmpty()) { + out << "[debug]\n"; + QStringList dbg = m_debugCategories.split(',', Qt::SkipEmptyParts); + for (const QString& d : dbg) { + int eq = d.indexOf('='); + if (eq > 0) out << d.left(eq).trimmed() << '=' << d.mid(eq + 1).trimmed() << '\n'; + } + out << '\n'; + } + out << "[allowed_keys]\n"; out << "allow_all=yes\n\n"; @@ -404,6 +417,14 @@ bool NodeConfig::save() { config_update_in_lines(lines, "gui", "debug_level", m_debugLevel.isEmpty() ? "info" : m_debugLevel, true); config_update_in_lines(lines, "gui", "debug_categories", m_debugCategories, true); + /* [debug] section: each category=level as separate line */ + QStringList dbg = m_debugCategories.split(',', Qt::SkipEmptyParts); + for (const QString& d : dbg) { + int eq = d.indexOf('='); + if (eq > 0) + config_update_in_lines(lines, "debug", d.left(eq).trimmed(), d.mid(eq + 1).trimmed(), true); + } + if (!f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) return false; QTextStream out(&f); for (int i = 0; i < lines.size(); i++)