package rag import ( "bytes" "encoding/json" "fmt" "io" "net/http" "strings" "time" ) type EmbeddingClient struct { apiKey string baseURL string client *http.Client } func NewEmbeddingClient(apiKey, baseURL string) *EmbeddingClient { if baseURL == "" { baseURL = "https://api.openai.com/v1" } return &EmbeddingClient{ apiKey: apiKey, baseURL: strings.TrimRight(baseURL, "/"), client: &http.Client{Timeout: 30 * time.Second}, } } type embeddingRequest struct { Model string `json:"model"` Input []string `json:"input"` } type embeddingResponse struct { Data []struct { Embedding []float64 `json:"embedding"` Index int `json:"index"` } `json:"data"` Usage struct { TotalTokens int `json:"total_tokens"` } `json:"usage"` } func (c *EmbeddingClient) Embed(texts []string, model string) ([][]float64, error) { if len(texts) == 0 { return nil, nil } if model == "" { model = "text-embedding-3-small" } body := embeddingRequest{ Model: model, Input: texts, } bodyBytes, err := json.Marshal(body) if err != nil { return nil, fmt.Errorf("marshal embedding request: %w", err) } url := c.baseURL + "/embeddings" req, err := http.NewRequest("POST", url, bytes.NewReader(bodyBytes)) if err != nil { return nil, fmt.Errorf("create embedding request: %w", err) } req.Header.Set("Content-Type", "application/json") if c.apiKey != "" { req.Header.Set("Authorization", "Bearer "+c.apiKey) } resp, err := c.client.Do(req) if err != nil { return nil, fmt.Errorf("send 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("parse embedding response: %w", err) } result := make([][]float64, len(texts)) for _, data := range embResp.Data { if data.Index < len(result) { result[data.Index] = data.Embedding } } return result, nil } func (c *EmbeddingClient) EmbedSingle(text, model string) ([]float64, error) { results, err := c.Embed([]string{text}, model) if err != nil { return nil, err } if len(results) == 0 { return nil, fmt.Errorf("no embedding returned") } return results[0], nil }