feat(ai-stack-2026): add OpenAPI 3.0.3 schema, Telegram /ai_doctor & /fix integration, AI language selector, and log deduplication

- Agent REST API (agent_api.uc): Added GET /openapi.json returning OpenAPI 3.0.3 spec for ChatGPT Custom GPTs, N8N, Dify, and LLM Agents
- AI Doctor (runtime.uc): Added compress_log_snippet() for deduplicating repeated log entries and ai_doctor_lang support (ru/en prompt selection)
- Telegram Bot (telegram.uc): Added /ai_doctor and /fix commands with Inline Keyboards for instant quick-fix execution
- Settings (renderAdvancedSettings.ts, settings.js): Added AI Doctor Language selector (ru/en)
- Documentation: Updated README.md and README.en.md with 2026 AI features
This commit is contained in:
Dushnilin
2026-08-11 16:10:31 +03:00
parent 6dd085b777
commit be5f41a52e
6 changed files with 330 additions and 24 deletions
+13 -1
View File
@@ -43,8 +43,20 @@ A feature-rich control center right inside your messenger:
* **Device Access Control**: View active DHCP clients and block/unblock internet access by MAC address. * **Device Access Control**: View active DHCP clients and block/unblock internet access by MAC address.
* **Network Diagnostics**: Trigger connection tests directly from Telegram. * **Network Diagnostics**: Trigger connection tests directly from Telegram.
### 🤖 AI Doctor & HTTP REST Agent API (2026 AI Stack)
* **Tachyon AI Doctor (v2)**: Advanced AI diagnostic engine supporting modern LLMs (**OpenAI**, **Anthropic Claude**, **DeepSeek**, and custom OpenAI-compatible servers/OpenRouter/Ollama).
* **Contextual Intelligence**: Feeds the LLM real-time service status, Watchdog failure history (OOM events, error streaks), and compressed system logs.
* **Multi-Fix Execution Chains**: Generates multi-action repair sequences with dedicated UI buttons per fix or a single "Fix All" action.
* **13 Built-in Quick Fix Codes**: Automated repairs for sing-box, nftables, dnsmasq, resolv.conf, DNS cache clearing, subscription refreshing, and network restarts.
* **Language Modes**: Configurable diagnosis output language (`ru` / `en`).
* **🤖 Interactive Telegram Commands (`/ai_doctor` & `/fix`)**:
* Trigger AI diagnostics directly from Telegram using `/ai_doctor` with interactive Inline Keyboards for instant quick-fix execution.
* **🌐 HTTP REST Agent API & OpenAPI 3.0 (Swagger)**:
* Full-featured API at `/cgi-bin/tachyon-agent/` with granular READ/WRITE permission controls secured by a Bearer token (`agent_api_token`).
* Native **OpenAPI 3.0.3** spec (`GET /cgi-bin/tachyon-agent/openapi.json`) and OpenAI Function Calling schema (`GET /tools`) for seamless integration with **ChatGPT Custom GPTs**, N8N, Dify, Flowise, and autonomous LLM agents (Cursor, AutoGPT, Claude Code).
### 🩺 System Self-Healing — Tachyon Doctor ### 🩺 System Self-Healing — Tachyon Doctor
An intelligent diagnostic and repair engine available via CLI (`tachyon doctor`) or WebUI: An intelligent local diagnostic and repair engine available via CLI (`tachyon doctor`) or WebUI:
* Scans all core services (`sing-box`, `zapret`, `zapret2`, `byedpi`, `dnsmasq`, `watchdog`). * Scans all core services (`sing-box`, `zapret`, `zapret2`, `byedpi`, `dnsmasq`, `watchdog`).
* Validates `nftables` chains and local DNS resolution health. * Validates `nftables` chains and local DNS resolution health.
* **Auto-Repair**: Automatically rebuilds broken firewall rules and restarts crashed services without requiring a full router reboot. * **Auto-Repair**: Automatically rebuilds broken firewall rules and restarts crashed services without requiring a full router reboot.
+13 -1
View File
@@ -43,8 +43,20 @@
* **Управление устройствами LAN**: Просмотр активных DHCP-клиентов и блокировка/разблокировка доступа в интернет по MAC-адресам. * **Управление устройствами LAN**: Просмотр активных DHCP-клиентов и блокировка/разблокировка доступа в интернет по MAC-адресам.
* **Диагностика сети**: Запуск тестов доступности прямо из чата. * **Диагностика сети**: Запуск тестов доступности прямо из чата.
### 🤖 ИИ-Доктор и HTTP REST Agent API (2026 AI Stack)
* **Tachyon AI Doctor (v2)**: Интеллектуальный диагностический модуль с поддержкой современных LLM (**OpenAI**, **Anthropic Claude**, **DeepSeek** и пользовательских OpenAI-совместимых серверов/OpenRouter/Ollama).
* **Контекстный анализ**: Передаёт в модель не только снимки служб, но и события Watchdog (OOM, серии ошибок) и последние сжатые логи роутера.
* **Многокомпонентные цепочки фиксов (Multi-Fix)**: Возвращает цепочку автоматических исправлений с возможностью выбора конкретного фикса или одной кнопкой «Исправить всё».
* **13 встроенных Quick Fix кодов**: Автоматический ремонт sing-box, nftables, dnsmasq, resolv.conf, очистка кэша DNS, обновление подписок и перезапуск сетевых служб.
* **Языковые режимы**: Динамический выбор языка вердикта (`ru` / `en`).
* **🤖 Управление через Telegram-бот (`/ai_doctor` & `/fix`)**:
* Запуск AI-диагностики прямо из мессенджера одной командой `/ai_doctor` с выведением интерактивных Inline-кнопок для моментального применения авто-исправлений.
* **🌐 HTTP REST Agent API & OpenAPI 3.0 (Swagger)**:
* Полноценный API по адресу `/cgi-bin/tachyon-agent/` с разделением READ/WRITE эндпоинтов и защиты Bearer-токеном (`agent_api_token`).
* Спецификация **OpenAPI 3.0.3** (`GET /cgi-bin/tachyon-agent/openapi.json`) и схема инструментов (`GET /tools`) для подсоединения к **ChatGPT Custom GPTs**, N8N, Dify, Flowise и автономным ИИ-агентам (Cursor, AutoGPT, Claude Code).
### 🩺 Системный авторемонт — Tachyon Doctor ### 🩺 Системный авторемонт — Tachyon Doctor
Встроенный модуль интеллектуальной диагностики и самовосстановления (`tachyon doctor` или из WebUI): Встроенный модуль локальной диагностики и самовосстановления (`tachyon doctor` или из WebUI):
* Сканирование состояния всех ключевых служб (`sing-box`, `zapret`, `zapret2`, `byedpi`, `dnsmasq`, `watchdog`). * Сканирование состояния всех ключевых служб (`sing-box`, `zapret`, `zapret2`, `byedpi`, `dnsmasq`, `watchdog`).
* Валидация правил файрвола `nftables` и проверка резолвинга DNS через `localhost`. * Валидация правил файрвола `nftables` и проверка резолвинга DNS через `localhost`.
* **Автоматическое исправление**: Самостоятельно устраняет сбои (пересоздает цепочки `nftables`, перезапускает упавшие сервисы) без полной перезагрузки устройства. * **Автоматическое исправление**: Самостоятельно устраняет сбои (пересоздает цепочки `nftables`, перезапускает упавшие сервисы) без полной перезагрузки устройства.
@@ -18,6 +18,7 @@ interface AdvancedSettingsState {
enableAiDoctor: boolean; enableAiDoctor: boolean;
aiDoctorProvider: string; aiDoctorProvider: string;
aiDoctorModel: string; aiDoctorModel: string;
aiDoctorLang: string;
aiDoctorApiKey: string; aiDoctorApiKey: string;
aiDoctorCustomUrl: string; aiDoctorCustomUrl: string;
aiWatchdog: { aiWatchdog: {
@@ -54,6 +55,7 @@ let _state: AdvancedSettingsState = {
enableAiDoctor: false, enableAiDoctor: false,
aiDoctorProvider: 'openai', aiDoctorProvider: 'openai',
aiDoctorModel: '', aiDoctorModel: '',
aiDoctorLang: 'ru',
aiDoctorApiKey: '', aiDoctorApiKey: '',
aiDoctorCustomUrl: '', aiDoctorCustomUrl: '',
aiWatchdog: { aiWatchdog: {
@@ -153,6 +155,9 @@ export async function loadAdvancedSettingsState() {
aiDoctorModel: aiDoctorModel:
((settingsSec as unknown as Record<string, unknown>) ((settingsSec as unknown as Record<string, unknown>)
?.ai_doctor_model as string) || '', ?.ai_doctor_model as string) || '',
aiDoctorLang:
((settingsSec as unknown as Record<string, unknown>)
?.ai_doctor_lang as string) || 'ru',
aiDoctorApiKey: aiDoctorApiKey:
((settingsSec as unknown as Record<string, unknown>) ((settingsSec as unknown as Record<string, unknown>)
?.ai_doctor_api_key as string) || '', ?.ai_doctor_api_key as string) || '',
@@ -311,6 +316,10 @@ async function saveAiWatchdogSettings() {
'set', 'set',
`${TACHYON_UCI_PACKAGE}.settings.ai_doctor_model=${_state.aiDoctorModel}`, `${TACHYON_UCI_PACKAGE}.settings.ai_doctor_model=${_state.aiDoctorModel}`,
], ],
[
'set',
`${TACHYON_UCI_PACKAGE}.settings.ai_doctor_lang=${_state.aiDoctorLang}`,
],
[ [
'set', 'set',
`${TACHYON_UCI_PACKAGE}.settings.ai_doctor_api_key=${_state.aiDoctorApiKey}`, `${TACHYON_UCI_PACKAGE}.settings.ai_doctor_api_key=${_state.aiDoctorApiKey}`,
@@ -826,6 +835,42 @@ function renderAiWatchdogSection(state: AdvancedSettingsState) {
}, },
}), }),
]), ]),
E('div', { class: 'tachyon_adv__row' }, [
E(
'label',
{ class: 'tachyon_adv__label' },
_('AI Doctor Language'),
),
E(
'select',
{
class: 'cbi-input-select',
onchange: (e: Event) => {
const val = (e.target as HTMLSelectElement).value;
_state = { ..._state, aiDoctorLang: val };
rerender();
},
},
[
E(
'option',
{
value: 'ru',
selected: state.aiDoctorLang === 'ru',
},
'Русский (Russian)',
),
E(
'option',
{
value: 'en',
selected: state.aiDoctorLang === 'en',
},
'English',
),
],
),
]),
E('div', { class: 'tachyon_adv__row' }, [ E('div', { class: 'tachyon_adv__row' }, [
E('label', { class: 'tachyon_adv__label' }, _('AI API Key')), E('label', { class: 'tachyon_adv__label' }, _('AI API Key')),
E('input', { E('input', {
@@ -1282,6 +1282,18 @@ function createSettingsContent(section, capabilities) {
o.rmempty = true; o.rmempty = true;
o.placeholder = _("(provider default)"); o.placeholder = _("(provider default)");
o = section.taboption(
"ai",
form.ListValue,
"ai_doctor_lang",
_("AI Doctor Language"),
_("Language for AI Doctor diagnosis responses"),
);
o.depends("enable_ai_doctor", "1");
o.value("ru", "Русский (Russian)");
o.value("en", "English");
o.default = "ru";
o = section.taboption( o = section.taboption(
"ai", "ai",
+86 -22
View File
@@ -2941,6 +2941,43 @@ function diagnose_json() {
return 0; return 0;
} }
function compress_log_snippet(raw_snippet) {
if (!raw_snippet || raw_snippet == "") return "No recent system errors logged.";
let lines = split(raw_snippet, "\n");
let compressed = [];
let prev_line = "";
let repeat_cnt = 1;
for (let i = 0; i < length(lines); i++) {
let line = trim(as_string(lines[i]));
if (line == "") continue;
let normalized = replace(line, /^[A-Z][a-z]{2}\s+\d+\s+\d+:\d+:\d+\s+[^\s]+\s+/, "");
normalized = replace(normalized, /^\d{4}-\d{2}-\d{2}\s+\d+:\d+:\d+\s+/, "");
if (normalized == prev_line) {
repeat_cnt++;
} else {
if (prev_line != "") {
if (repeat_cnt > 1) {
push(compressed, sprintf("%s [repeated %dx]", lines[i-1], repeat_cnt));
} else {
push(compressed, lines[i-1]);
}
}
prev_line = normalized;
repeat_cnt = 1;
}
}
if (prev_line != "" && length(lines) > 0) {
if (repeat_cnt > 1) {
push(compressed, sprintf("%s [repeated %dx]", lines[length(lines)-1], repeat_cnt));
} else {
push(compressed, lines[length(lines)-1]);
}
}
return join("\n", compressed);
}
function apply_quick_fix(codes_str) { function apply_quick_fix(codes_str) {
if (!codes_str || codes_str == "") { if (!codes_str || codes_str == "") {
print(sprintf("%J\n", { success: false, error: "No fix code provided" })); print(sprintf("%J\n", { success: false, error: "No fix code provided" }));
@@ -3083,7 +3120,8 @@ function ai_doctor() {
} }
} }
let log_snippet = trim(command_output("logread | grep -iE 'tachyon|sing-box|dnsmasq|oom|error|fatal' | tail -n 15 2>/dev/null")) || "No recent system errors logged."; let raw_log_snippet = trim(command_output("logread | grep -iE 'tachyon|sing-box|dnsmasq|oom|error|fatal' | tail -n 25 2>/dev/null")) || "No recent system errors logged.";
let log_snippet = compress_log_snippet(raw_log_snippet);
let sys_context = sprintf( let sys_context = sprintf(
"Tachyon Doctor Report:\n%s\n\n" + "Tachyon Doctor Report:\n%s\n\n" +
@@ -3098,27 +3136,53 @@ function ai_doctor() {
watchdog_info, log_snippet watchdog_info, log_snippet
); );
let prompt = sprintf( let lang = lc(trim(cfg.ai_doctor_lang || "ru"));
"Вы — ИИ-ассистент \"Tachyon AI Doctor\" для сервиса обхода блокировок на OpenWrt.\n" + let prompt = "";
"Проанализируйте диагностический отчет и логи ниже.\n\n%s\n\n" + if (lang == "en") {
"Сформулируйте краткий диагноз (на русском, максимум 3-4 пункта).\n" + prompt = sprintf(
"Если авто-исправление возможно, укажите в самом конце ответа строчку:\n" + "You are \"Tachyon AI Doctor\", an AI assistant for anti-censorship and proxy services on OpenWrt.\n" +
"FIX: код1, код2\n\n" + "Analyze the diagnostic report and system logs below.\n\n%s\n\n" +
"Доступные коды быстрого исправления:\n" + "Formulate a concise diagnosis (in English, max 3-4 bullet points).\n" +
"- start_singbox (sing-box упал или остановлен)\n" + "If automatic quick fix is possible, include at the very end of your response:\n" +
"- rebuild_rules (nftables или ip rule правила нарушены)\n" + "FIX: code1, code2\n\n" +
"- fix_dnsmasq (конфиг или сервис dnsmasq не отвечает)\n" + "Available quick fix codes:\n" +
"- fix_resolv_symlink (resolv.conf повреждён)\n" + "- start_singbox (sing-box process is stopped or missing)\n" +
"- start_watchdog (watchdog остановлен)\n" + "- rebuild_rules (nftables or ip rules damaged)\n" +
"- restart_singbox_dns (sing-box DNS не отвечает)\n" + "- fix_dnsmasq (dnsmasq service not responding)\n" +
"- fix_uci_config (конфиг Tachyon повреждён)\n" + "- fix_resolv_symlink (resolv.conf symlink broken)\n" +
"- fix_wan_interface (WAN интерфейс не работает)\n" + "- start_watchdog (watchdog daemon stopped)\n" +
"- fix_gateway (шлюз отсутствует)\n" + "- restart_singbox_dns (sing-box DNS failed)\n" +
"- clear_dns_cache (очистить кэш DNS и перезапустить dnsmasq)\n" + "- fix_uci_config (Tachyon UCI config corrupted)\n" +
"- update_subscriptions (принудительно обновить прокси подписки)\n" + "- fix_wan_interface (WAN interface down)\n" +
"- reset_firewall (перезапустить файрвол роутера)\n" + "- fix_gateway (default gateway missing)\n" +
"- restart_network (перезапустить сетевой стек)\n\n" + "- clear_dns_cache (clear DNS cache & restart dnsmasq)\n" +
"Если авто-исправление не применимо, не пишите тег FIX.", sys_context); "- update_subscriptions (force update proxy subscriptions)\n" +
"- reset_firewall (restart router firewall)\n" +
"- restart_network (restart network service)\n\n" +
"If no quick fix applies, do NOT output any FIX tag.", sys_context);
} else {
prompt = sprintf(
"Вы — ИИ-ассистент \"Tachyon AI Doctor\" для сервиса обхода блокировок на OpenWrt.\n" +
"Проанализируйте диагностический отчет и логи ниже.\n\n%s\n\n" +
"Сформулируйте краткий диагноз (на русском, максимум 3-4 пункта).\n" +
"Если авто-исправление возможно, укажите в самом конце ответа строчку:\n" +
"FIX: код1, код2\n\n" +
"Доступные коды быстрого исправления:\n" +
"- start_singbox (sing-box упал или остановлен)\n" +
"- rebuild_rules (nftables или ip rule правила нарушены)\n" +
"- fix_dnsmasq (конфиг или сервис dnsmasq не отвечает)\n" +
"- fix_resolv_symlink (resolv.conf повреждён)\n" +
"- start_watchdog (watchdog остановлен)\n" +
"- restart_singbox_dns (sing-box DNS не отвечает)\n" +
"- fix_uci_config (конфиг Tachyon повреждён)\n" +
"- fix_wan_interface (WAN интерфейс не работает)\n" +
"- fix_gateway (шлюз отсутствует)\n" +
"- clear_dns_cache (очистить кэш DNS и перезапустить dnsmasq)\n" +
"- update_subscriptions (принудительно обновить прокси подписки)\n" +
"- reset_firewall (перезапустить файрвол роутера)\n" +
"- restart_network (перезапустить сетевой стек)\n\n" +
"Если авто-исправление не применимо, не пишите тег FIX.", sys_context);
}
let provider = cfg.ai_doctor_provider || "openai"; let provider = cfg.ai_doctor_provider || "openai";
let api_key = cfg.ai_doctor_api_key || ""; let api_key = cfg.ai_doctor_api_key || "";
+161
View File
@@ -446,6 +446,165 @@ function handle_domain_add(body) {
ok({ message: "Domain '" + domain + "' added to section '" + section + "'" }); ok({ message: "Domain '" + domain + "' added to section '" + section + "'" });
} }
function handle_openapi() {
let spec = {
openapi: "3.0.3",
info: {
title: "Tachyon Router AI Agent API",
description: "OpenAPI 3.0 REST API for managing Tachyon anti-censorship orchestration & AI Doctor on OpenWrt routers. Supports ChatGPT Custom GPTs, N8N, Dify, and LLM Agents.",
version: "1.0.0"
},
servers: [
{
url: "/cgi-bin/tachyon-agent",
description: "Tachyon Router Agent API Gateway"
}
],
paths: {
"/health": {
get: {
summary: "System health check",
operationId: "getHealth",
responses: { "200": { description: "Health status" } }
}
},
"/snapshot": {
get: {
summary: "Full system state snapshot for LLM context",
operationId: "getSnapshot",
responses: { "200": { description: "Complete system snapshot" } }
}
},
"/diagnose": {
get: {
summary: "Run system diagnostics and auto-repair",
operationId: "getDiagnose",
responses: { "200": { description: "Diagnostic report" } }
}
},
"/logs": {
get: {
summary: "Get recent system log entries",
operationId: "getLogs",
responses: { "200": { description: "System log lines" } }
}
},
"/config": {
get: {
summary: "Get UCI configuration (secrets masked)",
operationId: "getConfig",
responses: { "200": { description: "UCI settings" } }
}
},
"/tools": {
get: {
summary: "Get tool definitions in OpenAI Function Calling format",
operationId: "getTools",
responses: { "200": { description: "Tool schemas" } }
}
},
"/ai-doctor/last": {
get: {
summary: "Get last AI Doctor diagnosis report",
operationId: "getAiDoctorLast",
responses: { "200": { description: "AI Doctor history report" } }
}
},
"/heal": {
post: {
summary: "Trigger autonomous repair cycle",
operationId: "postHeal",
security: [{ BearerAuth: [] }],
responses: { "200": { description: "Repair initiated" } }
}
},
"/ai-doctor/fix": {
post: {
summary: "Apply AI Doctor quick fix codes",
operationId: "postAiDoctorFix",
security: [{ BearerAuth: [] }],
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
properties: {
fix: { type: "string", example: "clear_dns_cache,start_singbox" }
}
}
}
}
},
responses: { "200": { description: "Quick fix results" } }
}
},
"/restart": {
post: {
summary: "Restart system service",
operationId: "postRestart",
security: [{ BearerAuth: [] }],
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
properties: {
service: { type: "string", enum: ["singbox", "tachyon", "dnsmasq", "watchdog"] }
}
}
}
}
},
responses: { "200": { description: "Restart outcome" } }
}
},
"/reload": {
post: {
summary: "Reload firewall rules & configs",
operationId: "postReload",
security: [{ BearerAuth: [] }],
responses: { "200": { description: "Reload status" } }
}
},
"/config/set": {
post: {
summary: "Set UCI configuration option",
operationId: "postConfigSet",
security: [{ BearerAuth: [] }],
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
properties: {
section: { type: "string" },
option: { type: "string" },
value: { type: "string" }
}
}
}
}
},
responses: { "200": { description: "UCI update result" } }
}
}
},
components: {
securitySchemes: {
BearerAuth: {
type: "http",
scheme: "bearer",
bearerFormat: "Secret Token"
}
}
}
};
write_json(spec);
}
function handle_ai_doctor_last() { function handle_ai_doctor_last() {
let raw = fs.readfile("/tmp/ai_doctor_last.json"); let raw = fs.readfile("/tmp/ai_doctor_last.json");
if (!raw) { if (!raw) {
@@ -504,6 +663,8 @@ if (method == "GET") {
handle_tools(); handle_tools();
else if (route == "/ai-doctor/last" || route == "/ai-doctor/last/") else if (route == "/ai-doctor/last" || route == "/ai-doctor/last/")
handle_ai_doctor_last(); handle_ai_doctor_last();
else if (route == "/openapi.json" || route == "/openapi.json/")
handle_openapi();
else else
err("Unknown endpoint: GET " + route, 404); err("Unknown endpoint: GET " + route, 404);
} else if (method == "POST") { } else if (method == "POST") {