Files
voice-to-notes/python/voice_to_notes/ipc/protocol.py

48 lines
1.3 KiB
Python
Raw Normal View History

Phase 1 foundation: Tauri shell, Python sidecar, SQLite database Tauri v2 + Svelte + TypeScript frontend: - App shell with workspace layout (waveform, transcript, speakers, AI chat) - Placeholder components for all major UI areas - Typed stores (project, transcript, playback, AI) - TypeScript interfaces matching the database schema - Tauri bridge service with typed invoke wrappers - svelte-check passes with 0 errors Rust backend: - Tauri v2 app entry point with command registration - SQLite database layer (rusqlite with bundled SQLite) - Full schema: projects, media_files, speakers, segments, words, ai_outputs, annotations (with indexes) - Model structs with serde serialization - CRUD queries for projects, speakers, segments, words - Segment text editing preserves original text - Schema versioning for future migrations - 6 tests passing - Command stubs for project, transcribe, export, AI, settings, system - App state management Python sidecar: - JSON-line IPC protocol (stdin/stdout) - Message types: IPCMessage, progress, error, ready - Handler registry with routing and error handling - Ping/pong handler for connectivity testing - Service stubs: transcribe, diarize, pipeline, AI, export - Provider stubs: local (llama-server), OpenAI, Anthropic, LiteLLM - Hardware detection stubs - 14 tests passing, ruff clean Also adds: - Testing strategy document (docs/TESTING.md) - Validation script (scripts/validate.sh) - Updated .gitignore for Svelte, Rust, Python artifacts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:16:06 -08:00
"""JSON-line protocol reader/writer over stdin/stdout."""
from __future__ import annotations
import json
import sys
from typing import Any
from voice_to_notes.ipc.messages import IPCMessage
def read_message() -> IPCMessage | None:
"""Read a single JSON-line message from stdin. Returns None on EOF."""
try:
line = sys.stdin.readline()
if not line:
return None # EOF
line = line.strip()
if not line:
return None
data = json.loads(line)
return IPCMessage.from_dict(data)
except json.JSONDecodeError as e:
_log(f"Invalid JSON: {e}")
return None
except Exception as e:
_log(f"Read error: {e}")
return None
def write_message(msg: IPCMessage) -> None:
"""Write a JSON-line message to stdout."""
line = json.dumps(msg.to_dict(), separators=(",", ":"))
sys.stdout.write(line + "\n")
sys.stdout.flush()
def write_dict(data: dict[str, Any]) -> None:
"""Write a raw dict as a JSON-line message to stdout."""
line = json.dumps(data, separators=(",", ":"))
sys.stdout.write(line + "\n")
sys.stdout.flush()
def _log(message: str) -> None:
"""Log to stderr (stdout is reserved for IPC)."""
print(f"[sidecar] {message}", file=sys.stderr, flush=True)