ModelTranscriptionLive loads the transcription backend, opens the bidirectional AudioTranscriptionLive RPC, sends the session config, and BLOCKS until the backend's ready ack. A grpcerrors. IsLiveTranscriptionUnsupported error means the backend (or the loaded model) cannot do live transcription and
(ctx context.Context, language string, ml *model.ModelLoader, modelConfig config.ModelConfig, appConfig *config.ApplicationConfig, onEvent func(LiveTranscriptionEvent))
| 203 | // background goroutine — in order, one event at a time — for every response |
| 204 | // the backend streams, ending with the Final event triggered by Close. |
| 205 | func ModelTranscriptionLive(ctx context.Context, language string, |
| 206 | ml *model.ModelLoader, modelConfig config.ModelConfig, appConfig *config.ApplicationConfig, |
| 207 | onEvent func(LiveTranscriptionEvent)) (LiveTranscriptionSession, error) { |
| 208 | |
| 209 | transcriptionModel, err := loadTranscriptionModel(ctx, ml, modelConfig, appConfig) |
| 210 | if err != nil { |
| 211 | return nil, err |
| 212 | } |
| 213 | |
| 214 | // The derived cancel out-lives this call inside the session: Close uses |
| 215 | // it to unwind the stream (and, in embed mode, the server-side recv |
| 216 | // pump, which only stops on send-close or context cancellation). |
| 217 | streamCtx, cancel := context.WithCancel(ctx) |
| 218 | stream, err := transcriptionModel.AudioTranscriptionLive(streamCtx) |
| 219 | if err != nil { |
| 220 | cancel() |
| 221 | return nil, err |
| 222 | } |
| 223 | |
| 224 | fail := func(err error) (LiveTranscriptionSession, error) { |
| 225 | _ = stream.CloseSend() |
| 226 | cancel() |
| 227 | return nil, err |
| 228 | } |
| 229 | |
| 230 | if err := stream.Send(&proto.TranscriptLiveRequest{ |
| 231 | Payload: &proto.TranscriptLiveRequest_Config{Config: &proto.TranscriptLiveConfig{ |
| 232 | Language: language, |
| 233 | SampleRate: liveSampleRate, |
| 234 | }}, |
| 235 | }); err != nil { |
| 236 | return fail(err) |
| 237 | } |
| 238 | |
| 239 | // Ready-ack contract: the backend answers a successful open with a |
| 240 | // {ready:true} response before any transcript data; unsupported |
| 241 | // backends surface Unimplemented here instead. |
| 242 | ack, err := stream.Recv() |
| 243 | if err != nil { |
| 244 | return fail(err) |
| 245 | } |
| 246 | if !ack.GetReady() { |
| 247 | return fail(fmt.Errorf("live transcription: backend %q broke the ready-ack contract (first response carried data)", modelConfig.Backend)) |
| 248 | } |
| 249 | |
| 250 | s := &liveTranscriptionSession{ |
| 251 | stream: stream, |
| 252 | cancel: cancel, |
| 253 | recvDone: make(chan struct{}), |
| 254 | trace: newLiveTraceState(modelConfig, appConfig, language), |
| 255 | } |
| 256 | |
| 257 | go func() { |
| 258 | defer close(s.recvDone) |
| 259 | for { |
| 260 | resp, err := stream.Recv() |
| 261 | if err != nil { |
| 262 | if !errors.Is(err, io.EOF) && streamCtx.Err() == nil { |
no test coverage detected