J.A.R.V.I.S-rust/resources/commands/ocr/read_screen.lua

83 lines
3.2 KiB
Lua
Raw Normal View History

feat: LLM auto-fallback + codegen pack + OCR pack LLM auto-fallback (jarvis-app/src/app.rs + jarvis-core/src/config.rs): - When neither intent classifier nor levenshtein finds a command match, route the utterance straight to the LLM instead of just playing the "not found" sound. Triggers ("скажи X", "answer Y") still work and take precedence — they short-circuit before command lookup. - Two new config knobs: LLM_AUTO_FALLBACK = true — master toggle LLM_AUTO_FALLBACK_MIN_CHARS = 4 — suppress for very short utterances so background noise doesn't burn Groq quota - Requires GROQ_TOKEN; if absent, behaviour is unchanged (play_not_found). codegen/ command pack: - Phrases: "напиши код X", "сгенерируй скрипт Y", "write code Z", etc. - Strips the trigger from the recognized phrase, sends what remains to Groq with a strict system prompt ("return ONLY code, no fences, no commentary") at temperature 0.2. - Parses the JSON content, unescapes \n / \" / \\ / \t, strips any remaining ```lang ... ``` fences, drops the result into the clipboard via jarvis.system.clipboard.set. - Notifies a 120-char preview + plays ok-sound. Works for any language the model handles (Python by default if unspecified). - GROQ_TOKEN / GROQ_MODEL / GROQ_BASE_URL read from env at call time — same envvars the voice-loop fallback already uses. ocr/ command pack: - Phrases: "прочитай экран", "что на экране", "read screen", etc. - Captures the primary screen via System.Windows.Forms + System.Drawing to a temp PNG, then shells to tesseract.exe (-l rus+eng for ru/ua, -l eng for en). - Resolves Tesseract by PATH first, then C:\Program Files\Tesseract-OCR and the x86 install dir; if none works the user gets a friendly "winget install UB-Mannheim.TesseractOCR" hint in a notification. - Recognized text goes to clipboard + a 200-char preview notification. All three features are portable (env-var resolution, no hardcoded user paths). Command-pack total is now 11. cargo test -p jarvis-core --lib commands::tests passes 3/3.
2026-05-15 01:39:21 +03:00
local lang = jarvis.context.language
local temp = jarvis.system.env("TEMP") or jarvis.system.env("TMP") or "C:\\Windows\\Temp"
local ts = jarvis.context.time.year .. jarvis.context.time.month .. jarvis.context.time.day
.. "_" .. jarvis.context.time.hour .. jarvis.context.time.minute .. jarvis.context.time.second
local img = temp .. "\\jarvis_ocr_" .. ts .. ".png"
local capture_ps = string.format(
[[Add-Type -AssemblyName System.Windows.Forms,System.Drawing; $b=[System.Windows.Forms.Screen]::PrimaryScreen.Bounds; $bmp=New-Object Drawing.Bitmap $b.Width,$b.Height; $g=[Drawing.Graphics]::FromImage($bmp); $g.CopyFromScreen($b.Location,[Drawing.Point]::Empty,$b.Size); $bmp.Save('%s'); $g.Dispose(); $bmp.Dispose()]],
img:gsub("\\", "\\\\")
)
local capture_cmd = string.format('powershell -NoProfile -ExecutionPolicy Bypass -Command "%s"', capture_ps:gsub('"', '\\"'))
local cap = jarvis.system.exec(capture_cmd)
if not cap.success or not jarvis.fs.exists(img) then
jarvis.log("error", "screen capture failed: " .. tostring(cap.stderr))
jarvis.system.notify("OCR", lang == "ru" and "Не смог снять экран" or "Could not capture screen")
jarvis.audio.play_error()
return { chain = false }
end
local tesseract_paths = {
"tesseract.exe",
"C:\\Program Files\\Tesseract-OCR\\tesseract.exe",
"C:\\Program Files (x86)\\Tesseract-OCR\\tesseract.exe",
}
local tess = nil
for _, p in ipairs(tesseract_paths) do
local check
if p == "tesseract.exe" then
check = jarvis.system.exec("where tesseract.exe")
else
check = { success = jarvis.fs.exists(p), stdout = p }
end
if check.success and (check.stdout or ""):gsub("[\r\n]+$", "") ~= "" then
tess = p == "tesseract.exe" and "tesseract.exe" or '"' .. p .. '"'
break
end
end
if not tess then
jarvis.log("error", "tesseract not found")
jarvis.system.notify(
"OCR",
lang == "ru"
and "Tesseract не установлен. Установи: winget install UB-Mannheim.TesseractOCR"
or "Tesseract not installed. Install: winget install UB-Mannheim.TesseractOCR"
)
jarvis.audio.play_error()
return { chain = false }
end
local langs = lang == "en" and "eng" or "rus+eng"
local ocr_cmd = string.format('%s "%s" - -l %s', tess, img, langs)
local ocr = jarvis.system.exec(ocr_cmd)
if not ocr.success then
jarvis.log("error", "tesseract failed: " .. tostring(ocr.stderr):sub(1, 200))
jarvis.system.notify("OCR", lang == "ru" and "Tesseract упал" or "Tesseract failed")
jarvis.audio.play_error()
return { chain = false }
end
local text = (ocr.stdout or ""):gsub("^%s+", ""):gsub("%s+$", "")
if text == "" then
jarvis.system.notify("OCR", lang == "ru" and "Текста не найдено" or "No text found")
jarvis.audio.play_not_found()
return { chain = false }
end
jarvis.system.clipboard.set(text)
jarvis.log("info", "OCR result (" .. #text .. " bytes) copied to clipboard")
local preview = text:gsub("\r?\n", " "):sub(1, 200)
if #text > 200 then preview = preview .. "..." end
jarvis.system.notify(
lang == "ru" and "Текст с экрана" or "Screen text",
preview
)
jarvis.audio.play_ok()
return { chain = false }