feat: Tauri backend commands + wikipedia rewrite + github PR review pack
GUI integration
- crates/jarvis-gui/src/tauri_commands/backends.rs — 5 new Tauri commands:
get_active_backends() -> {tts, llm, llm_model, profile}
set_llm_backend(name) -> swap LLM hot, persists to DB
set_tts_backend(name) -> persist choice (effective on jarvis-app restart)
llm_reset_context() -> clear conversation history
llm_get_last_reply() -> last assistant message or null
- Registered in main.rs invoke_handler. Svelte side can now read active
backend + swap from a settings page / footer without env-var gymnastics.
Wikipedia pack rewritten (resources/commands/wikipedia/)
- Old `wiki/wiki.lua` (104 lines, hand-rolled Groq HTTP + inline SAPI PS) deleted.
- Replacement `wikipedia/summary.lua` (87 lines):
* REST summary endpoint (cleaner than opensearch+summary chain).
* Russian first, English fallback with LLM translation.
* Pure LLM fallback if Wikipedia has no article.
* Uses jarvis.cmd.{ok,error,not_found} + jarvis.llm + jarvis.speak (no
inline PowerShell SAPI). Phrases kept compatible with old pack.
GitHub PR voice review (resources/commands/github_pr/, 3 commands)
- "текущий репо bossiara13/J.A.R.V.I.S-rust" → github.set_repo
Persists repo via jarvis.memory.
- "какие пиары" / "открытые пиары" → github.list_prs
Calls `gh pr list --json number,title,author,createdAt --limit 10`.
Parses count + first 3 titles by regex from JSON.
- "разбери последний пиар" / "что в pr" → github.summarize_pr
`gh pr view N --json title,body,additions,deletions,changedFiles,author`,
sends to LLM with "senior reviewer" system prompt, speaks 3-5 sentence
review focusing on changes, risks, merge-readiness.
- Requires gh CLI installed and `gh auth login` done.
Tests: 52/52 jarvis-core unit tests pass (Wikipedia pack TOML/script auto-checked).
Build: cargo build --release -p jarvis-app -p jarvis-gui green.
This commit is contained in:
parent
385bd5c8ce
commit
b243e67870
11 changed files with 439 additions and 131 deletions
40
resources/commands/wikipedia/command.toml
Normal file
40
resources/commands/wikipedia/command.toml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# Wikipedia summary — fetch a topic's intro from Wikipedia REST API, speak it.
|
||||
|
||||
[[commands]]
|
||||
id = "wikipedia.summary"
|
||||
type = "lua"
|
||||
script = "summary.lua"
|
||||
sandbox = "full"
|
||||
timeout = 15000
|
||||
|
||||
[commands.phrases]
|
||||
ru = [
|
||||
"что такое",
|
||||
"кто такой",
|
||||
"кто такая",
|
||||
"расскажи про",
|
||||
"википедия",
|
||||
"найди в википедии",
|
||||
"что говорит википедия",
|
||||
"поищи в википедии",
|
||||
"википедия о",
|
||||
"википедия про",
|
||||
"найди в википедии про",
|
||||
"статья из википедии",
|
||||
]
|
||||
en = [
|
||||
"wikipedia",
|
||||
"look up in wikipedia",
|
||||
"wikipedia article on",
|
||||
"what is",
|
||||
"who is",
|
||||
"tell me about",
|
||||
"wikipedia about",
|
||||
]
|
||||
ua = [
|
||||
"вікіпедія",
|
||||
"знайди у вікіпедії",
|
||||
"що таке",
|
||||
"хто такий",
|
||||
"розкажи про",
|
||||
]
|
||||
105
resources/commands/wikipedia/summary.lua
Normal file
105
resources/commands/wikipedia/summary.lua
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
-- "Википедия Эйнштейн" → fetch ru.wikipedia.org/api/rest_v1/page/summary/<topic>
|
||||
-- Falls back to LLM if Wikipedia doesn't have the article.
|
||||
local phrase = (jarvis.context.phrase or "")
|
||||
|
||||
local query = jarvis.text.strip_trigger(phrase:lower(), {
|
||||
"найди в википедии про",
|
||||
"найди в википедии",
|
||||
"что говорит википедия",
|
||||
"поищи в википедии",
|
||||
"статья из википедии",
|
||||
"википедия про",
|
||||
"википедия о",
|
||||
"википедия",
|
||||
"расскажи про",
|
||||
"что такое",
|
||||
"кто такая",
|
||||
"кто такой",
|
||||
"look up in wikipedia",
|
||||
"wikipedia article on",
|
||||
"wikipedia about",
|
||||
"wikipedia",
|
||||
"tell me about",
|
||||
"what is",
|
||||
"who is",
|
||||
"знайди у вікіпедії",
|
||||
"вікіпедія",
|
||||
"що таке",
|
||||
"хто такий",
|
||||
"розкажи про",
|
||||
})
|
||||
query = query:gsub("^[%s,:%.]+", ""):gsub("%s+$", "")
|
||||
|
||||
if query == "" then
|
||||
return jarvis.cmd.error("Какая статья?")
|
||||
end
|
||||
|
||||
-- Wikipedia title needs underscores for spaces. Capitalize first letter of each word
|
||||
-- to match canonical titles (good enough heuristic).
|
||||
local title = query:gsub("(%S+)", function(w)
|
||||
-- Capitalize first byte (works for ASCII; for cyrillic, leave alone).
|
||||
local first = w:sub(1, 1)
|
||||
if first:match("[%a]") then
|
||||
return first:upper() .. w:sub(2)
|
||||
end
|
||||
return w
|
||||
end):gsub(" ", "_")
|
||||
|
||||
local function urlencode(s)
|
||||
return (s:gsub("[^A-Za-z0-9%-_%.~]", function(c)
|
||||
return string.format("%%%02X", string.byte(c))
|
||||
end))
|
||||
end
|
||||
|
||||
-- Try Russian first, fall back to English.
|
||||
local lang_chain = { "ru", "en" }
|
||||
local data, source_lang
|
||||
|
||||
for _, lang in ipairs(lang_chain) do
|
||||
local url = string.format("https://%s.wikipedia.org/api/rest_v1/page/summary/%s",
|
||||
lang, urlencode(title))
|
||||
local d = jarvis.http.json(url)
|
||||
if d and d.extract and d.extract ~= "" then
|
||||
data = d
|
||||
source_lang = lang
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not data then
|
||||
-- last resort: ask LLM directly
|
||||
local llm_answer = jarvis.llm({
|
||||
{ role = "system", content = "Кратко (2-3 предложения) на русском объясни тему. Если не знаешь — скажи." },
|
||||
{ role = "user", content = "Расскажи про: " .. query }
|
||||
}, { max_tokens = 200, temperature = 0.3 })
|
||||
if llm_answer and llm_answer ~= "" then
|
||||
return jarvis.cmd.ok(llm_answer)
|
||||
end
|
||||
return jarvis.cmd.not_found("В Википедии нет статьи про " .. query .. ".")
|
||||
end
|
||||
|
||||
local extract = data.extract
|
||||
local title_actual = data.title or query
|
||||
|
||||
-- Truncate extract to ~3 sentences for spoken output
|
||||
local sentences = {}
|
||||
for sentence in extract:gmatch("[^%.%!%?]+[%.%!%?]") do
|
||||
table.insert(sentences, sentence)
|
||||
if #sentences >= 3 then break end
|
||||
end
|
||||
local spoken = #sentences > 0 and table.concat(sentences) or extract:sub(1, 400)
|
||||
|
||||
if source_lang == "en" then
|
||||
-- Translate English extract to Russian via LLM (no point in speaking English
|
||||
-- through a Russian voice).
|
||||
local translated = jarvis.llm({
|
||||
{ role = "system", content = "Переведи английский текст на русский, без вводных и комментариев." },
|
||||
{ role = "user", content = spoken }
|
||||
}, { max_tokens = 300, temperature = 0.0 })
|
||||
if translated and translated ~= "" then
|
||||
spoken = translated
|
||||
end
|
||||
end
|
||||
|
||||
jarvis.system.notify("Wikipedia: " .. title_actual, spoken:sub(1, 200))
|
||||
return jarvis.cmd.ok(spoken)
|
||||
Loading…
Add table
Add a link
Reference in a new issue