A small helpdesk system: an office Pixel running a patched GrapheneOS Dialer answers technician calls, records both call legs as separate channels, and a Ruby backend transcribes them through Whisper and files an AI summary against the caller. Squashed to a single commit for sharing. No credentials are included; secrets live outside the repo in /etc/helpdesk/env on the server or a gitignored .claude/env.local locally. See .claude/env.local.example for the shape. Start at README.md, then docs/architecture.md.
61 lines
2.5 KiB
Ruby
61 lines
2.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# Zero-dependency test for the Transcriber wrapper. Uses tiny Ruby STUB scripts in place of the
|
|
# real transcribe.py, so the subprocess/exit-code/output logic is validated in milliseconds — no
|
|
# 7 GB model, no CPU-minutes. (A real end-to-end run is a separate manual smoke.)
|
|
# Run: ruby components/transcription-worker/test_transcriber.rb
|
|
|
|
require_relative "transcriber"
|
|
require "tmpdir"
|
|
|
|
$pass = 0; $fail = 0
|
|
def ok(d); $pass += 1; puts " ok #{d}"; end
|
|
def bad(d, g = nil); $fail += 1; puts " FAIL #{d}#{g.nil? ? '' : " (#{g.inspect})"}"; end
|
|
def eq(a, b, d); a == b ? ok(d) : bad(d, a); end
|
|
def truthy(x, d); x ? ok(d) : bad(d, x); end
|
|
|
|
Dir.mktmpdir do |dir|
|
|
# a stub that writes a [Speaker N] transcript to the -o path and exits with a chosen code
|
|
write_stub = lambda do |name, ruby|
|
|
p = File.join(dir, name); File.write(p, ruby); p
|
|
end
|
|
ok_stub = write_stub.call("ok.rb", <<~RB)
|
|
o = ARGV[ARGV.index("-o") + 1]
|
|
File.write(o, "[Speaker 1]: Ahoj.\\n[Speaker 2]: Nazdar.\\n")
|
|
exit 0
|
|
RB
|
|
empty_stub = write_stub.call("empty.rb", "o=ARGV[ARGV.index('-o')+1]; File.write(o,''); exit 0\n")
|
|
gated_stub = write_stub.call("gated.rb", "exit 5\n") # HF terms not accepted (terminal)
|
|
crash_stub = write_stub.call("crash.rb", "exit 137\n") # OOM-kill style (retryable)
|
|
slow_stub = write_stub.call("slow.rb", "sleep 5\n") # to exercise the timeout
|
|
|
|
tr = ->(stub, **kw) { Helpdesk::Transcriber.new(python: "ruby", script: stub, env_file: File.join(dir, ".none")).transcribe(File.join(dir, "audio.wav"), **kw) }
|
|
|
|
puts "success path"
|
|
r = tr.call(ok_stub)
|
|
eq(r[:state], "transcribed", "exit 0 with output → transcribed")
|
|
truthy(r[:transcript].include?("Ahoj"), "transcript captured from -o file")
|
|
eq(r[:retryable], false, "success not retryable")
|
|
|
|
puts "no-speech (empty output on exit 0)"
|
|
eq(tr.call(empty_stub)[:state], "no_speech", "empty transcript → no_speech (not a failure)")
|
|
|
|
puts "terminal config error"
|
|
r = tr.call(gated_stub)
|
|
eq(r[:state], "config_error", "exit 5 → config_error")
|
|
eq(r[:retryable], false, "config error NOT retried")
|
|
|
|
puts "retryable crash"
|
|
r = tr.call(crash_stub)
|
|
eq(r[:state], "failed", "exit 137 → failed")
|
|
eq(r[:retryable], true, "crash/OOM IS retryable")
|
|
|
|
puts "timeout kills the process and is retryable"
|
|
r = tr.call(slow_stub, timeout: 1)
|
|
eq(r[:state], "failed", "timeout → failed")
|
|
eq(r[:retryable], true, "timeout is retryable")
|
|
eq(r[:reason], "timeout", "reason=timeout")
|
|
end
|
|
|
|
puts "\n#{$pass} passed, #{$fail} failed"
|
|
exit($fail.zero? ? 0 : 1)
|