Ctrl K
ComunidadGuías…

internal/llm/capability_embedding_client.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package llm

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"github.com/yeomyeonggeori/blueclaw/internal/capability"
	"github.com/yeomyeonggeori/blueclaw/internal/config"
)

const DefaultEmbeddingModelName = "google/embeddinggemma-2"

// DefaultEmbeddingDimensions is the width DefaultEmbeddingModelName answers
// in. A store embedded at one width cannot be searched at another, so the
// model and its width are declared together.
const DefaultEmbeddingDimensions = 768

func ConfiguredEmbeddingModelName(runtimeConfiguration config.RuntimeConfiguration) string {
	return firstNonEmpty(
		strings.TrimSpace(runtimeConfiguration.LanguageModel.Embedding.Model),
		strings.TrimSpace(runtimeConfiguration.Memory.EmbeddingModel),
		DefaultEmbeddingModelName,
	)
}

func ConfiguredEmbeddingDimensions(runtimeConfiguration config.RuntimeConfiguration) int {
	if runtimeConfiguration.Memory.EmbeddingDimensions > 0 {
		return runtimeConfiguration.Memory.EmbeddingDimensions
	}
	return DefaultEmbeddingDimensions
}

const (
	EmbeddingInputTypeQuery    = "query"
	EmbeddingInputTypeDocument = "document"
)

type CapabilityEmbeddingClient struct {
	CapabilityClient capability.Client
	ModelName        string
	ExecutionMode    string
	OutputDimensions int
}

func (client CapabilityEmbeddingClient) EmbeddingModelName() string {
	return client.ModelName
}

type EmbeddingInput struct {
	Text      string
	InputType string
}

type capabilityEmbeddingRequestDocument struct {
	Input            any    `json:"input"`
	Model            string `json:"model,omitempty"`
	ExecutionMode    string `json:"executionMode,omitempty"`
	InputType        string `json:"inputType,omitempty"`
	OutputDimensions int    `json:"outputDimensions,omitempty"`
}

type capabilityEmbeddingResponseDocument struct {
	Provider        string      `json:"provider"`
	Model           string      `json:"model"`
	SelectedBackend string      `json:"selectedBackend"`
	Embedding       []float64   `json:"embedding"`
	Embeddings      [][]float64 `json:"embeddings"`
}

func (client CapabilityEmbeddingClient) GenerateEmbeddingForInput(ctx context.Context, input EmbeddingInput) ([]float32, error) {
	responseDocument, errorValue := client.post(ctx, input.Text, input.InputType)
	if errorValue != nil {
		return nil, errorValue
	}
	if len(responseDocument.Embedding) == 0 {
		return nil, errors.New("capability embedding response carried no embedding")
	}
	return float32Embedding(responseDocument.Embedding), nil
}

func (client CapabilityEmbeddingClient) GenerateEmbeddings(ctx context.Context, texts []string, inputType string) ([][]float32, error) {
	if len(texts) == 0 {
		return [][]float32{}, nil
	}
	responseDocument, errorValue := client.post(ctx, texts, inputType)
	if errorValue != nil {
		return nil, errorValue
	}
	if len(responseDocument.Embeddings) != len(texts) {
		return nil, fmt.Errorf("capability embedding response carried %d embeddings for %d inputs", len(responseDocument.Embeddings), len(texts))
	}
	embeddings := make([][]float32, 0, len(texts))
	for _, embedding := range responseDocument.Embeddings {
		embeddings = append(embeddings, float32Embedding(embedding))
	}
	return embeddings, nil
}

func (client CapabilityEmbeddingClient) EmbedQuery(ctx context.Context, text string) ([]float32, error) {
	return client.GenerateEmbeddingForInput(ctx, EmbeddingInput{Text: text, InputType: EmbeddingInputTypeQuery})
}

func (client CapabilityEmbeddingClient) EmbedDocuments(ctx context.Context, texts []string) ([][]float32, error) {
	return client.GenerateEmbeddings(ctx, texts, EmbeddingInputTypeDocument)
}

func (client CapabilityEmbeddingClient) post(ctx context.Context, input any, inputType string) (capabilityEmbeddingResponseDocument, error) {
	if client.CapabilityClient.HTTPClient == nil {
		return capabilityEmbeddingResponseDocument{}, errors.New("capability embedding http client is not configured")
	}
	var responseDocument capabilityEmbeddingResponseDocument
	errorValue := client.CapabilityClient.PostJSON(
		ctx,
		"/v1/embedding/create",
		capabilityEmbeddingRequestDocument{
			Input:            input,
			Model:            client.ModelName,
			ExecutionMode:    firstNonEmpty(client.ExecutionMode, "auto"),
			InputType:        inputType,
			OutputDimensions: client.OutputDimensions,
		},
		&responseDocument,
	)
	return responseDocument, errorValue
}

func float32Embedding(values []float64) []float32 {
	embedding := make([]float32, 0, len(values))
	for _, value := range values {
		embedding = append(embedding, float32(value))
	}
	return embedding
}