Files
handler-base/messages/messages.go
Billy D. 35912d5844 feat: add e2e tests, perf benchmarks, and infrastructure improvements
- messages/bench_test.go: serialization benchmarks (msgpack map vs struct vs protobuf)
- clients/clients_test.go: HTTP client tests with pooling verification (20 tests)
- natsutil/natsutil_test.go: encode/decode roundtrip + binary data tests
- handler/handler_test.go: handler dispatch tests + benchmark
- config/config.go: live reload via fsnotify + RWMutex getter methods
- clients/clients.go: SharedTransport + sync.Pool buffer pooling
- messages/messages.go: typed structs with msgpack+json tags
- messages/proto/: protobuf schema + generated code

Benchmark baseline (ChatRequest roundtrip):
  MsgpackMap:    2949 ns/op, 36 allocs
  MsgpackStruct: 2030 ns/op, 13 allocs (31% faster, 64% fewer allocs)
  Protobuf:       793 ns/op,  8 allocs (73% faster, 78% fewer allocs)
2026-02-20 06:44:37 -05:00

225 lines
12 KiB
Go

// Package messages defines typed NATS message structs for all services.
//
// Using typed structs with short msgpack field tags instead of map[string]any
// provides compile-time safety, smaller wire size (integer-like short keys vs
// full string keys), and faster encode/decode by avoiding interface{} boxing.
//
// Audio data uses raw []byte instead of base64-encoded strings — msgpack
// supports binary natively, eliminating the 33% base64 overhead.
package messages
import "time"
// ────────────────────────────────────────────────────────────────────────────
// Pipeline Bridge
// ────────────────────────────────────────────────────────────────────────────
// PipelineTrigger is the request to start a pipeline.
type PipelineTrigger struct {
RequestID string `msgpack:"request_id" json:"request_id"`
Pipeline string `msgpack:"pipeline" json:"pipeline"`
Parameters map[string]any `msgpack:"parameters,omitempty" json:"parameters,omitempty"`
}
// PipelineStatus is the response / status update for a pipeline run.
type PipelineStatus struct {
RequestID string `msgpack:"request_id" json:"request_id"`
Status string `msgpack:"status" json:"status"`
RunID string `msgpack:"run_id,omitempty" json:"run_id,omitempty"`
Engine string `msgpack:"engine,omitempty" json:"engine,omitempty"`
Pipeline string `msgpack:"pipeline,omitempty" json:"pipeline,omitempty"`
SubmittedAt string `msgpack:"submitted_at,omitempty" json:"submitted_at,omitempty"`
Error string `msgpack:"error,omitempty" json:"error,omitempty"`
AvailablePipelines []string `msgpack:"available_pipelines,omitempty" json:"available_pipelines,omitempty"`
}
// ────────────────────────────────────────────────────────────────────────────
// Chat Handler
// ────────────────────────────────────────────────────────────────────────────
// ChatRequest is an incoming chat message.
type ChatRequest struct {
RequestID string `msgpack:"request_id" json:"request_id"`
UserID string `msgpack:"user_id" json:"user_id"`
Message string `msgpack:"message" json:"message"`
Query string `msgpack:"query,omitempty" json:"query,omitempty"`
Premium bool `msgpack:"premium,omitempty" json:"premium,omitempty"`
EnableRAG bool `msgpack:"enable_rag,omitempty" json:"enable_rag,omitempty"`
EnableReranker bool `msgpack:"enable_reranker,omitempty" json:"enable_reranker,omitempty"`
EnableStreaming bool `msgpack:"enable_streaming,omitempty" json:"enable_streaming,omitempty"`
TopK int `msgpack:"top_k,omitempty" json:"top_k,omitempty"`
Collection string `msgpack:"collection,omitempty" json:"collection,omitempty"`
EnableTTS bool `msgpack:"enable_tts,omitempty" json:"enable_tts,omitempty"`
SystemPrompt string `msgpack:"system_prompt,omitempty" json:"system_prompt,omitempty"`
ResponseSubject string `msgpack:"response_subject,omitempty" json:"response_subject,omitempty"`
}
// EffectiveQuery returns Message or falls back to Query.
func (c *ChatRequest) EffectiveQuery() string {
if c.Message != "" {
return c.Message
}
return c.Query
}
// ChatResponse is the full reply to a chat request.
type ChatResponse struct {
UserID string `msgpack:"user_id" json:"user_id"`
Response string `msgpack:"response" json:"response"`
ResponseText string `msgpack:"response_text" json:"response_text"`
UsedRAG bool `msgpack:"used_rag" json:"used_rag"`
RAGSources []string `msgpack:"rag_sources,omitempty" json:"rag_sources,omitempty"`
Success bool `msgpack:"success" json:"success"`
Audio []byte `msgpack:"audio,omitempty" json:"audio,omitempty"`
Error string `msgpack:"error,omitempty" json:"error,omitempty"`
}
// ChatStreamChunk is a single streaming chunk from an LLM response.
type ChatStreamChunk struct {
RequestID string `msgpack:"request_id" json:"request_id"`
Type string `msgpack:"type" json:"type"`
Content string `msgpack:"content" json:"content"`
Done bool `msgpack:"done" json:"done"`
Timestamp int64 `msgpack:"timestamp" json:"timestamp"`
}
// ────────────────────────────────────────────────────────────────────────────
// Voice Assistant
// ────────────────────────────────────────────────────────────────────────────
// VoiceRequest is an incoming voice-to-voice request.
type VoiceRequest struct {
RequestID string `msgpack:"request_id" json:"request_id"`
Audio []byte `msgpack:"audio" json:"audio"`
Language string `msgpack:"language,omitempty" json:"language,omitempty"`
Collection string `msgpack:"collection,omitempty" json:"collection,omitempty"`
}
// VoiceResponse is the reply to a voice request.
type VoiceResponse struct {
RequestID string `msgpack:"request_id" json:"request_id"`
Response string `msgpack:"response" json:"response"`
Audio []byte `msgpack:"audio" json:"audio"`
Transcription string `msgpack:"transcription,omitempty" json:"transcription,omitempty"`
Sources []DocumentSource `msgpack:"sources,omitempty" json:"sources,omitempty"`
Error string `msgpack:"error,omitempty" json:"error,omitempty"`
}
// DocumentSource is a RAG search result source.
type DocumentSource struct {
Text string `msgpack:"text" json:"text"`
Score float64 `msgpack:"score" json:"score"`
}
// ────────────────────────────────────────────────────────────────────────────
// TTS Module
// ────────────────────────────────────────────────────────────────────────────
// TTSRequest is a text-to-speech synthesis request.
type TTSRequest struct {
Text string `msgpack:"text" json:"text"`
Speaker string `msgpack:"speaker,omitempty" json:"speaker,omitempty"`
Language string `msgpack:"language,omitempty" json:"language,omitempty"`
SpeakerWavB64 string `msgpack:"speaker_wav_b64,omitempty" json:"speaker_wav_b64,omitempty"`
Stream bool `msgpack:"stream,omitempty" json:"stream,omitempty"`
}
// TTSAudioChunk is a streamed audio chunk from TTS synthesis.
type TTSAudioChunk struct {
SessionID string `msgpack:"session_id" json:"session_id"`
ChunkIndex int `msgpack:"chunk_index" json:"chunk_index"`
TotalChunks int `msgpack:"total_chunks" json:"total_chunks"`
Audio []byte `msgpack:"audio" json:"audio"`
IsLast bool `msgpack:"is_last" json:"is_last"`
Timestamp int64 `msgpack:"timestamp" json:"timestamp"`
SampleRate int `msgpack:"sample_rate" json:"sample_rate"`
}
// TTSFullResponse is a non-streamed TTS response (whole audio).
type TTSFullResponse struct {
SessionID string `msgpack:"session_id" json:"session_id"`
Audio []byte `msgpack:"audio" json:"audio"`
Timestamp int64 `msgpack:"timestamp" json:"timestamp"`
SampleRate int `msgpack:"sample_rate" json:"sample_rate"`
}
// TTSStatus is a TTS processing status update.
type TTSStatus struct {
SessionID string `msgpack:"session_id" json:"session_id"`
Status string `msgpack:"status" json:"status"`
Message string `msgpack:"message" json:"message"`
Timestamp int64 `msgpack:"timestamp" json:"timestamp"`
}
// TTSVoiceListResponse is the reply to a voice list request.
type TTSVoiceListResponse struct {
DefaultSpeaker string `msgpack:"default_speaker" json:"default_speaker"`
CustomVoices []TTSVoiceInfo `msgpack:"custom_voices" json:"custom_voices"`
LastRefresh int64 `msgpack:"last_refresh" json:"last_refresh"`
Timestamp int64 `msgpack:"timestamp" json:"timestamp"`
}
// TTSVoiceInfo is summary info about a custom voice.
type TTSVoiceInfo struct {
Name string `msgpack:"name" json:"name"`
Language string `msgpack:"language" json:"language"`
ModelType string `msgpack:"model_type" json:"model_type"`
CreatedAt string `msgpack:"created_at" json:"created_at"`
}
// TTSVoiceRefreshResponse is the reply to a voice refresh request.
type TTSVoiceRefreshResponse struct {
Count int `msgpack:"count" json:"count"`
CustomVoices []TTSVoiceInfo `msgpack:"custom_voices" json:"custom_voices"`
Timestamp int64 `msgpack:"timestamp" json:"timestamp"`
}
// ────────────────────────────────────────────────────────────────────────────
// STT Module
// ────────────────────────────────────────────────────────────────────────────
// STTStreamMessage is any message on the ai.voice.stream.{session} subject.
type STTStreamMessage struct {
Type string `msgpack:"type" json:"type"`
Audio []byte `msgpack:"audio,omitempty" json:"audio,omitempty"`
State string `msgpack:"state,omitempty" json:"state,omitempty"`
SpeakerID string `msgpack:"speaker_id,omitempty" json:"speaker_id,omitempty"`
}
// STTTranscription is the transcription result published by the STT module.
type STTTranscription struct {
SessionID string `msgpack:"session_id" json:"session_id"`
Transcript string `msgpack:"transcript" json:"transcript"`
Sequence int `msgpack:"sequence" json:"sequence"`
IsPartial bool `msgpack:"is_partial" json:"is_partial"`
IsFinal bool `msgpack:"is_final" json:"is_final"`
Timestamp int64 `msgpack:"timestamp" json:"timestamp"`
SpeakerID string `msgpack:"speaker_id" json:"speaker_id"`
HasVoiceActivity bool `msgpack:"has_voice_activity" json:"has_voice_activity"`
State string `msgpack:"state" json:"state"`
}
// STTInterrupt is published when the STT module detects a user interrupt.
type STTInterrupt struct {
SessionID string `msgpack:"session_id" json:"session_id"`
Type string `msgpack:"type" json:"type"`
Timestamp int64 `msgpack:"timestamp" json:"timestamp"`
SpeakerID string `msgpack:"speaker_id" json:"speaker_id"`
}
// ────────────────────────────────────────────────────────────────────────────
// Common / Error
// ────────────────────────────────────────────────────────────────────────────
// ErrorResponse is the standard error reply from any handler.
type ErrorResponse struct {
Error bool `msgpack:"error" json:"error"`
Message string `msgpack:"message" json:"message"`
Type string `msgpack:"type" json:"type"`
}
// Timestamp returns the current Unix timestamp (helper for message construction).
func Timestamp() int64 {
return time.Now().Unix()
}