Skip to content

Instantly share code, notes, and snippets.

@phumberdroz
Created March 23, 2026 04:26
Show Gist options
  • Select an option

  • Save phumberdroz/e51b9ad76b1604226265befb9a9cd5ea to your computer and use it in GitHub Desktop.

Select an option

Save phumberdroz/e51b9ad76b1604226265befb9a9cd5ea to your computer and use it in GitHub Desktop.
[output.post_process]
command = "~/.config/voxtype/gemini-cleanup.sh"
timeout_ms = 12000
GEMINI_API_KEY='<REDACTED>'
#!/usr/bin/env bash
set -euo pipefail
CONFIG_DIR="${HOME}/.config/voxtype"
ENV_FILE="${CONFIG_DIR}/gemini.env"
API_URL="https://generativelanguage.googleapis.com/v1beta/models"
MODEL="${VOXTYPE_GEMINI_MODEL:-gemini-2.5-flash-lite}"
TIMEOUT_SECONDS="${VOXTYPE_GEMINI_TIMEOUT_SECONDS:-10}"
STATE_DIR="${XDG_STATE_HOME:-${HOME}/.local/state}/voxtype/post-process"
LOG_ENABLED="${VOXTYPE_GEMINI_LOG_ENABLED:-1}"
if [[ -f "${ENV_FILE}" ]]; then
# shellcheck disable=SC1090
source "${ENV_FILE}"
fi
INPUT_TEXT="$(cat)"
if [[ -z "${INPUT_TEXT//[[:space:]]/}" ]]; then
exit 0
fi
if [[ -z "${GEMINI_API_KEY:-}" ]]; then
printf '%s' "${INPUT_TEXT}"
exit 0
fi
PROMPT=$'You are cleaning dictation for direct insertion into an app.\n\nRules:\n- Preserve meaning, intent, tone, and level of detail.\n- Make the smallest possible edits.\n- Remove filler words, false starts, repeated fragments, and obvious self-corrections only when they are clearly accidental.\n- Do not summarize, compress, reorder, or rewrite for style unless needed for clarity.\n- Keep wording close to the transcript when it already reads naturally.\n- Fix punctuation, capitalization, spacing, and paragraph breaks.\n- Correct obvious ASR mistakes only when the intended text is clear from context.\n- If the content naturally wants a list, format it as a clean list. Otherwise keep normal prose.\n- Do not add new facts, explanations, titles, markdown fences, or emojis.\n- If you are not confident a phrase should be changed, leave it as-is.\n- Output only the cleaned text.'
JSON_PAYLOAD="$(jq -n \
--arg prompt "${PROMPT}" \
--arg transcript "${INPUT_TEXT}" \
'{
systemInstruction: {
parts: [
{ text: "You clean raw speech-to-text output for low-latency dictation." }
]
},
contents: [
{
role: "user",
parts: [
{ text: ($prompt + "\n\nTranscript:\n" + $transcript) }
]
}
],
generationConfig: {
temperature: 0.05,
topP: 0.95,
maxOutputTokens: 8192
}
}')"
cleanup() {
[[ -n "${RESP_FILE:-}" && -f "${RESP_FILE}" ]] && rm -f "${RESP_FILE}"
[[ -n "${INPUT_FILE:-}" && -f "${INPUT_FILE}" ]] && rm -f "${INPUT_FILE}"
[[ -n "${OUTPUT_FILE:-}" && -f "${OUTPUT_FILE}" ]] && rm -f "${OUTPUT_FILE}"
[[ -n "${DIFF_FILE:-}" && -f "${DIFF_FILE}" ]] && rm -f "${DIFF_FILE}"
}
trap cleanup EXIT
RESP_FILE="$(mktemp)"
INPUT_FILE="$(mktemp)"
OUTPUT_FILE="$(mktemp)"
DIFF_FILE="$(mktemp)"
printf '%s' "${INPUT_TEXT}" > "${INPUT_FILE}"
TIMESTAMP="$(date -u +%Y%m%dT%H%M%SZ)"
RUN_ID="${TIMESTAMP}-$$"
LOG_DIR="${STATE_DIR}/${RUN_ID}"
START_MS="$(date +%s%3N)"
write_log_bundle() {
local status="$1"
local elapsed_ms="$2"
[[ "${LOG_ENABLED}" == "0" ]] && return 0
mkdir -p "${LOG_DIR}"
if ! diff -u "${INPUT_FILE}" "${OUTPUT_FILE}" > "${DIFF_FILE}"; then
true
fi
cat > "${LOG_DIR}/single.md" <<EOF
# Voxtype Post-Process Log
## Metadata
timestamp_utc=${TIMESTAMP}
model=${MODEL}
timeout_seconds=${TIMEOUT_SECONDS}
status=${status}
elapsed_ms=${elapsed_ms}
## Before
\`\`\`text
$(cat "${INPUT_FILE}")
\`\`\`
## After
\`\`\`text
$(cat "${OUTPUT_FILE}")
\`\`\`
## Diff
\`\`\`diff
$(cat "${DIFF_FILE}")
\`\`\`
EOF
}
if ! curl -fsS \
--max-time "${TIMEOUT_SECONDS}" \
-X POST \
"${API_URL}/${MODEL}:generateContent?key=${GEMINI_API_KEY}" \
-H 'Content-Type: application/json' \
-d "${JSON_PAYLOAD}" > "${RESP_FILE}"; then
printf '%s' "${INPUT_TEXT}" > "${OUTPUT_FILE}"
END_MS="$(date +%s%3N)"
write_log_bundle "curl_error_fallback" "$((END_MS-START_MS))"
printf '%s' "${INPUT_TEXT}"
exit 0
fi
OUTPUT_TEXT="$(jq -r '.candidates[0].content.parts[0].text // empty' "${RESP_FILE}")"
if [[ -z "${OUTPUT_TEXT//[[:space:]]/}" ]]; then
printf '%s' "${INPUT_TEXT}" > "${OUTPUT_FILE}"
END_MS="$(date +%s%3N)"
write_log_bundle "empty_response_fallback" "$((END_MS-START_MS))"
printf '%s' "${INPUT_TEXT}"
exit 0
fi
printf '%s' "${OUTPUT_TEXT}" > "${OUTPUT_FILE}"
END_MS="$(date +%s%3N)"
write_log_bundle "ok" "$((END_MS-START_MS))"
printf '%s' "${OUTPUT_TEXT}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment