# Google Chirp STT

> Configure Google Cloud Speech-to-Text with Chirp 2 for excellent Indic language support and reliable transcription.

Source: https://edesy.in/docs/voice-agent/stt/google

Google Cloud Speech-to-Text with Chirp 2 provides excellent accuracy for 125+ languages, with particular strength in Indic languages.

## Why Google Chirp?

| Feature | Google Chirp 2 | Deepgram Nova-3 |
|---------|---------------|-----------------|
| Languages | 125+ | 35+ |
| Indic Language Accuracy | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| Time to First Partial | ~120ms | ~80ms |
| Streaming | Full support | Full support |
| Cost | $0.016/min | $0.0043/min |

**Best for**: Hindi, Tamil, Telugu, Bengali, and other Indic languages.

## Configuration

### Basic Setup

```json
{
  "agent": {
    "name": "Hindi Support",
    "language": "hi-IN",
    "sttProvider": "google",
    "sttModel": "chirp_2"
  }
}
```

### Environment Variables

```bash
GOOGLE_CREDENTIALS_PATH=/path/to/service-account.json
# Or
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
```

### Advanced Configuration

```json
{
  "sttProvider": "google",
  "sttModel": "chirp_2",
  "sttConfig": {
    "languageCode": "hi-IN",
    "alternativeLanguageCodes": ["en-IN"],
    "enableAutomaticPunctuation": true,
    "enableSpokenPunctuation": false,
    "enableSpokenEmojis": false,
    "model": "chirp_2",
    "useEnhanced": true,
    "singleUtterance": false,
    "interimResults": true
  }
}
```

## Model Comparison

| Model | Accuracy | Latency | Languages | Use Case |
|-------|----------|---------|-----------|----------|
| chirp_2 | ⭐⭐⭐⭐⭐ | Fast | 125+ | Indic languages |
| chirp | ⭐⭐⭐⭐ | Fast | 100+ | General multilingual |
| latest_long | ⭐⭐⭐⭐ | Moderate | 125+ | Long-form audio |
| latest_short | ⭐⭐⭐⭐ | Fast | 125+ | Short utterances |
| telephony | ⭐⭐⭐ | Fast | 50+ | Phone audio quality |
| command_and_search | ⭐⭐⭐ | Fastest | 50+ | Commands only |

## Implementation

### Streaming Recognition

```go
type GoogleSTT struct {
    client   *speech.Client
    config   *speechpb.RecognitionConfig
    language string
}

func NewGoogleSTT(language string) (*GoogleSTT, error) {
    ctx := context.Background()
    client, err := speech.NewClient(ctx)
    if err != nil {
        return nil, err
    }

    config := &speechpb.RecognitionConfig{
        Encoding:                   speechpb.RecognitionConfig_LINEAR16,
        SampleRateHertz:            8000,
        LanguageCode:               language,
        Model:                      "chirp_2",
        UseEnhanced:                true,
        EnableAutomaticPunctuation: true,
    }

    return &GoogleSTT{
        client:   client,
        config:   config,
        language: language,
    }, nil
}

func (g *GoogleSTT) StreamRecognize(ctx context.Context) (*StreamSession, error) {
    stream, err := g.client.StreamingRecognize(ctx)
    if err != nil {
        return nil, err
    }

    // Send initial config
    streamingConfig := &speechpb.StreamingRecognitionConfig{
        Config:          g.config,
        InterimResults:  true,
        SingleUtterance: false,
    }

    if err := stream.Send(&speechpb.StreamingRecognizeRequest{
        StreamingRequest: &speechpb.StreamingRecognizeRequest_StreamingConfig{
            StreamingConfig: streamingConfig,
        },
    }); err != nil {
        return nil, err
    }

    return &StreamSession{stream: stream}, nil
}
```

### Sending Audio

```go
func (s *StreamSession) SendAudio(audio []byte) error {
    return s.stream.Send(&speechpb.StreamingRecognizeRequest{
        StreamingRequest: &speechpb.StreamingRecognizeRequest_AudioContent{
            AudioContent: audio,
        },
    })
}
```

### Receiving Results

```go
func (s *StreamSession) ReceiveResults() <-chan TranscriptEvent {
    results := make(chan TranscriptEvent)

    go func() {
        defer close(results)

        for {
            resp, err := s.stream.Recv()
            if err == io.EOF {
                return
            }
            if err != nil {
                return
            }

            for _, result := range resp.Results {
                if len(result.Alternatives) == 0 {
                    continue
                }

                alt := result.Alternatives[0]
                results <- TranscriptEvent{
                    Text:       alt.Transcript,
                    Confidence: alt.Confidence,
                    IsFinal:    result.IsFinal,
                    Stability:  result.Stability,
                }
            }
        }
    }()

    return results
}
```

## Language Support

### Indic Languages (Excellent)

| Language | Code | Accuracy | Notes |
|----------|------|----------|-------|
| Hindi | hi-IN | ⭐⭐⭐⭐⭐ | Best-in-class |
| Bengali | bn-IN | ⭐⭐⭐⭐⭐ | Excellent |
| Tamil | ta-IN | ⭐⭐⭐⭐⭐ | Excellent |
| Telugu | te-IN | ⭐⭐⭐⭐⭐ | Excellent |
| Marathi | mr-IN | ⭐⭐⭐⭐ | Very good |
| Gujarati | gu-IN | ⭐⭐⭐⭐ | Very good |
| Kannada | kn-IN | ⭐⭐⭐⭐ | Very good |
| Malayalam | ml-IN | ⭐⭐⭐⭐ | Very good |
| Punjabi | pa-IN | ⭐⭐⭐⭐ | Very good |
| Odia | or-IN | ⭐⭐⭐ | Good |
| Assamese | as-IN | ⭐⭐⭐ | Good |

### Multi-Language Detection

```json
{
  "sttConfig": {
    "languageCode": "hi-IN",
    "alternativeLanguageCodes": ["en-IN", "mr-IN"],
    "enableLanguageIdentification": true
  }
}
```

## Speech Adaptation

Improve accuracy for domain-specific terms:

```go
config := &speechpb.RecognitionConfig{
    // ... base config
    Adaptation: &speechpb.SpeechAdaptation{
        PhraseSets: []*speechpb.SpeechAdaptation_AdaptationPhraseSet{
            {
                Value: &speechpb.SpeechAdaptation_AdaptationPhraseSet_InlinePhraseSet{
                    InlinePhraseSet: &speechpb.PhraseSet{
                        Phrases: []*speechpb.PhraseSet_Phrase{
                            {Value: "Edesy", Boost: 20},
                            {Value: "voice agent", Boost: 15},
                            {Value: "STT", Boost: 10},
                        },
                    },
                },
            },
        },
    },
}
```

## Endpointing Configuration

```go
streamingConfig := &speechpb.StreamingRecognitionConfig{
    Config: config,
    StreamingFeatures: &speechpb.StreamingRecognitionFeatures{
        InterimResults: true,
        VoiceActivityTimeout: &speechpb.StreamingRecognitionFeatures_VoiceActivityTimeout{
            SpeechStartTimeout:  durationpb.New(5 * time.Second),
            SpeechEndTimeout:    durationpb.New(1 * time.Second),
        },
    },
}
```

## Error Handling

```go
func (g *GoogleSTT) handleError(err error) {
    status, ok := status.FromError(err)
    if !ok {
        log.Printf("Unknown error: %v", err)
        return
    }

    switch status.Code() {
    case codes.InvalidArgument:
        log.Printf("Invalid audio format or config")
    case codes.ResourceExhausted:
        log.Printf("Quota exceeded, implement backoff")
    case codes.Unavailable:
        log.Printf("Service unavailable, reconnecting...")
        g.reconnect()
    case codes.DeadlineExceeded:
        log.Printf("Request timeout")
    }
}
```

## Cost Optimization

### Pricing (per minute)

| Model | Standard | Data Logging |
|-------|----------|--------------|
| Chirp 2 | $0.016 | $0.012 |
| Enhanced | $0.024 | $0.018 |
| Standard | $0.006 | $0.004 |

### Optimization Tips

1. **Use appropriate model**: Chirp 2 for Indic, standard for English
2. **Enable data logging**: 25% cost reduction
3. **Batch short utterances**: Minimum billing is 15 seconds

```go
// Track usage for cost monitoring
func (g *GoogleSTT) trackUsage(audioLength time.Duration) {
    // Minimum billing is 15 seconds
    billedSeconds := max(15, int(audioLength.Seconds()))

    metrics.RecordCounter("stt.google.billed_seconds", int64(billedSeconds))
    metrics.RecordCounter("stt.google.cost_usd", float64(billedSeconds)/60*0.016)
}
```

## Best Practices

### 1. Handle Streaming Limits

Google limits streaming sessions to 5 minutes:

```go
func (g *GoogleSTT) maintainSession(ctx context.Context) {
    ticker := time.NewTicker(4 * time.Minute)
    defer ticker.Stop()

    for {
        select {
        case <-ticker.C:
            // Reconnect before 5-minute limit
            g.reconnect()
        case <-ctx.Done():
            return
        }
    }
}
```

### 2. Use Single Utterance for Short Commands

```json
{
  "sttConfig": {
    "singleUtterance": true
  }
}
```

### 3. Enable Enhanced Model for Telephony

```json
{
  "sttConfig": {
    "model": "telephony",
    "useEnhanced": true
  }
}
```

## Troubleshooting

| Issue | Cause | Solution |
|-------|-------|----------|
| No results | Wrong audio format | Verify LINEAR16, 8kHz mono |
| Low accuracy | Wrong model | Use chirp_2 for Indic |
| Session timeout | 5-minute limit | Implement auto-reconnect |
| High latency | Network issues | Use regional endpoint |

## Next Steps

- [Azure Speech](https://edesy.in/docs/voice-agent/stt/azure) - Enterprise alternative
- [Deepgram](https://edesy.in/docs/voice-agent/stt/deepgram) - Lower latency option
- [Language Support](https://edesy.in/docs/voice-agent/languages) - Full language matrix
