Files
st-pierre/embed.go
T
2026-05-31 12:35:40 -04:00

97 lines
2.1 KiB
Go

package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
)
const defaultModel = "text-embedding-3-small"
const defaultBaseURL = "https://api.openai.com/v1"
type embedder struct {
apiKey string
model string
baseURL string
httpClient *http.Client
}
func newEmbedder(apiKey, model, baseURL string) *embedder {
if model == "" {
model = defaultModel
}
if baseURL == "" {
baseURL = defaultBaseURL
}
return &embedder{
apiKey: apiKey,
model: model,
baseURL: strings.TrimRight(baseURL, "/"),
httpClient: &http.Client{
Timeout: 30 * time.Second,
},
}
}
type embeddingRequest struct {
Input string `json:"input"`
Model string `json:"model"`
EncodingFormat string `json:"encoding_format,omitempty"`
}
type embeddingResponse struct {
Data []struct {
Embedding []float32 `json:"embedding"`
} `json:"data"`
}
func (e *embedder) embedText(ctx context.Context, text string) ([]float32, error) {
reqBody := embeddingRequest{
Input: text,
Model: e.model,
}
body, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("marshal embedding request: %w", err)
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, e.baseURL+"/embeddings", bytes.NewReader(body))
if err != nil {
return nil, fmt.Errorf("create embedding request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+e.apiKey)
resp, err := e.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("embedding request: %w", err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read embedding response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("embedding API error %d: %s", resp.StatusCode, string(respBody))
}
var embResp embeddingResponse
if err := json.Unmarshal(respBody, &embResp); err != nil {
return nil, fmt.Errorf("unmarshal embedding response: %w", err)
}
if len(embResp.Data) == 0 {
return nil, fmt.Errorf("no embedding returned")
}
return embResp.Data[0].Embedding, nil
}