Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Ahnlich Go Client SDK

Ahnlich TestSuite Go Client Tag & Deploy

Table of Contents

Installation

Using Go modules:

go get github.com/deven96/ahnlich/sdk/ahnlich-client-go@vX.Y.Z

or add to your go.mod:

require (
    github.com/deven96/ahnlich/sdk/ahnlich-client-go vX.Y.Z
)

Package Information

This module provides:

  • gRPC service stubs for DB and AI
  • Pipeline utilities for batching RPC calls

Server Response

All DB query/response types live under:

import "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/db"

All AI query/response types live under:

import "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/ai"

Initialization

Client

DB Client

package example
import (
    "context"
    "fmt"
    "log"
    "time"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"

    algorithm "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/algorithm/algorithms"
    dbsvc     "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/services/db_service"
    dbquery   "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/db/query"
    keyval    "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/keyval"
    metadata  "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/metadata"
    predicates "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/predicates"
)

const ServerAddr = "127.0.0.1:1369"

// ExampleDBClient holds the gRPC connection and client.
type ExampleDBClient struct {
    conn   *grpc.ClientConn
    client dbsvc.DBServiceClient
}

// NewDBClient connects to the Ahnlich DB server.
func NewDBClient(ctx context.Context) (*ExampleDBClient, error) {
    conn, err := grpc.DialContext(
        ctx,
        ServerAddr,
        grpc.WithTransportCredentials(insecure.NewCredentials()),
        grpc.WithBlock(),
    )
    if err != nil {
        return nil, fmt.Errorf("failed to dial DB server %q: %w", ServerAddr, err)
    }
    client := dbsvc.NewDBServiceClient(conn)
    return &ExampleDBClient{conn: conn, client: client}, nil
}

// Close closes the gRPC connection.
func (c *ExampleDBClient) Close() error {
    return c.conn.Close()
}

AI Client

import (
    "context"
    "fmt"
    "time"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"

    algorithm "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/algorithm/algorithms"
    aisvc      "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/services/ai_service"
    aiquery    "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/ai/query"
    aimodel    "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/ai/models"
    keyval     "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/keyval"
    metadata   "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/metadata"
    preprocess "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/ai/preprocess"
    predicates "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/predicates"
)

const AIAddr = "127.0.0.1:1370"

// ExampleAIClient holds the gRPC connection and AI client.
type ExampleAIClient struct {
    conn   *grpc.ClientConn
    client aisvc.AIServiceClient
}

// NewAIClient connects to the Ahnlich AI server.
func NewAIClient(ctx context.Context) (*ExampleAIClient, error) {
    conn, err := grpc.DialContext(
        ctx,
        AIAddr,
        grpc.WithTransportCredentials(insecure.NewCredentials()),
        grpc.WithBlock(),
    )
    if err != nil {
        return nil, fmt.Errorf("failed to dial AI server %q: %w", AIAddr, err)
    }
    client := aisvc.NewAIServiceClient(conn)
    return &ExampleAIClient{conn: conn, client: client}, nil
}

// Close closes the gRPC connection.
func (c *ExampleAIClient) Close() error {
    return c.conn.Close()
}

Connection Pooling

Go’s gRPC client reuses connections under the hood. For advanced pooling, wrap the *grpc.ClientConn accordingly.

Requests - DB

Ping

func (c *ExampleDBClient) examplePingDB() error {
    resp, err := c.client.Ping(c.ctx, &dbquery.Ping{})
    if err != nil { return err }
    fmt.Println("Ping:", resp)
    return nil
}

Info Server

func (c *ExampleDBClient) exampleInfoServer() error {
    resp, err := c.client.InfoServer(c.ctx, &dbquery.InfoServer{})
    fmt.Println("InfoServer:", resp)
    return err
}

List Stores

func (c *ExampleDBClient) exampleListStores() error {
    resp, err := c.client.ListStores(c.ctx, &dbquery.ListStores{})
    if err != nil { return err }
    for _, s := range resp.Stores {
        // Each StoreInfo includes Name, Len, SizeInBytes, NonLinearIndices,
        // PredicateIndices ([]string), and Dimension (uint32).
        fmt.Printf("Store: %s, Dimension: %d, PredicateIndices: %v\n",
            s.Name, s.Dimension, s.PredicateIndices)
    }
    return nil
}

Get Store

func (c *ExampleDBClient) exampleGetStore() error {
    resp, err := c.client.GetStore(c.ctx, &dbquery.GetStore{Store: "my_store"})
    if err != nil { return err }
    // resp is a *StoreInfo with Name, Len, SizeInBytes, NonLinearIndices,
    // PredicateIndices ([]string), and Dimension (uint32).
    fmt.Printf("Store: %s, Dimension: %d, PredicateIndices: %v\n",
        resp.Name, resp.Dimension, resp.PredicateIndices)
    return nil
}

Create Store

func (c *ExampleDBClient) exampleCreateStore() error {
    _, err := c.client.CreateStore(c.ctx, &dbquery.CreateStore{Store:"my_store", Dimension:4})
    return err
}

Set

func (c *ExampleDBClient) exampleSet() error {
    entries := []*keyval.DbStoreEntry{{Key:&keyval.StoreKey{Key:[]float32{1,2,3,4}},Value:&keyval.StoreValue{Value:map[string]*metadata.MetadataValue{"label":{Value:&metadata.MetadataValue_RawString{RawString:"A"}}}}}}
    _, err := c.client.Set(c.ctx, &dbquery.Set{Store:"my_store", Inputs:entries})
    return err
}

Get Sim N

func (c *ExampleDBClient) exampleGetSimN() error {
    resp, err := c.client.GetSimN(c.ctx, &dbquery.GetSimN{Store:"my_store", SearchInput:&keyval.StoreKey{Key:[]float32{1,2,3,4}}, ClosestN:3})
    fmt.Println("GetSimN:", resp.Entries)
    return err
}

Get Key

func (c *ExampleDBClient) exampleGetKey() error {
    resp, err := c.client.GetKey(c.ctx, &dbquery.GetKey{Store:"my_store", Keys:[]*keyval.StoreKey{{Key:[]float32{1,2,3,4}}}})
    fmt.Println("GetKey:", resp.Entries)
    return err
}

Get By Predicate

func (c *ExampleDBClient) exampleGetByPredicate() error {
    cond := &predicates.PredicateCondition{Kind:&predicates.PredicateCondition_Value{Value:&predicates.Predicate{Kind:&predicates.Predicate_Equals{Equals:&predicates.Equals{Key:"label",Value:&metadata.MetadataValue{Value:&metadata.MetadataValue_RawString{RawString:"A"}}}}}}}
    resp, err := c.client.GetByPredicate(c.ctx, &dbquery.GetByPredicate{Store:"my_store", Condition:cond})
    fmt.Println("GetByPredicate:", resp.Entries)
    return err
}

Create Predicate Index

func (c *ExampleDBClient) exampleCreatePredicateIndex() error {
    _, err := c.client.CreatePredicateIndex(c.ctx, &dbquery.CreatePredIndex{Store:"my_store", Predicates:[]string{"label"}})
    return err
} 

Drop Predicate Index

func (c *ExampleDBClient) exampleDropPredicateIndex() error {
    _, err := c.client.DropPredicateIndex(c.ctx, &dbquery.DropPredIndex{Store:"my_store", Predicates:[]string{"label"}, ErrorIfNotExists:true})
    return err
}

Delete Key

func (c *ExampleDBClient) exampleDeleteKey() error {
    _, err := c.client.DelKey(c.ctx, &dbquery.DelKey{Store:"my_store", Keys:[]*keyval.StoreKey{{Key:[]float32{1,2,3,4}}}})
    return err
}

Drop Store

func (c *ExampleDBClient) exampleDropStore() error {
    _, err := c.client.DropStore(c.ctx, &dbquery.DropStore{Store: "my_store"})
    return err
}

List Connected Clients

func (c *ExampleDBClient) exampleListConnectedClients() error {
    resp, err := c.client.ListClients(c.ctx, &dbquery.ListClients{})
    if err != nil {
        return err
    }
    fmt.Println("Connected Clients:", resp.Clients)
    return nil
}

Create Non Linear Algorithm Index

import (
    nonlinear "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/algorithm/nonlinear"
)

func (c *ExampleDBClient) exampleCreateNonLinearAlgoIndex() error {
    // Create a KDTree index
    _, err := c.client.CreateNonLinearAlgorithmIndex(c.ctx, &dbquery.CreateNonLinearAlgorithmIndex{
        Store: "my_store",
        NonLinearIndices: []*nonlinear.NonLinearIndex{
            {Index: &nonlinear.NonLinearIndex_Kdtree{Kdtree: &nonlinear.KDTreeConfig{}}},
        },
    })
    if err != nil { return err }

    // Or create an HNSW index (with optional config)
    _, err = c.client.CreateNonLinearAlgorithmIndex(c.ctx, &dbquery.CreateNonLinearAlgorithmIndex{
        Store: "my_store",
        NonLinearIndices: []*nonlinear.NonLinearIndex{
            {Index: &nonlinear.NonLinearIndex_Hnsw{Hnsw: &nonlinear.HNSWConfig{}}},
        },
    })
    return err
}

Drop Non Linear Algorithm Index

func (c *ExampleDBClient) exampleDropNonLinearAlgoIndex() error {
    _, err := c.client.DropNonLinearAlgorithmIndex(c.ctx, &dbquery.DropNonLinearAlgorithmIndex{
        Store:            "my_store",
        NonLinearIndices: []nonlinear.NonLinearAlgorithm{nonlinear.NonLinearAlgorithm_KDTree},
        ErrorIfNotExists: true,
    })
    return err
}

Delete Predicate

func (c *ExampleDBClient) exampleDeletePredicate() error {
    cond := &predicates.PredicateCondition{
        Kind: &predicates.PredicateCondition_Value{Value: &predicates.Predicate{
            Kind: &predicates.Predicate_Equals{Equals: &predicates.Equals{
                Key:   "label",
                Value: &metadata.MetadataValue{Value: &metadata.MetadataValue_RawString{RawString: "A"}},
            }},
        }},
    }
    _, err := c.client.DelPred(c.ctx, &dbquery.DelPred{
        Store:     "my_store",
        Condition: cond,
    })
    return err
}

Requests - AI

Ping

func (c *ExampleAIClient) examplePingAI(ctx context.Context) error {
    resp, err := c.client.Ping(c.ctx, &aiquery.Ping{})
    if err != nil {
        return err
    }
    fmt.Println("AI Ping:", resp)
    return nil
}

Info Server

func (c *ExampleAIClient) exampleInfoServerAI(ctx context.Context) error {
    resp, err := c.client.InfoServer(c.ctx, &aiquery.InfoServer{})
    if err != nil {
        return err
    }
    fmt.Println("AI InfoServer:", resp)
    return nil
}

List Stores

func (c *ExampleAIClient) exampleListStoresAI(ctx context.Context) error {
    resp, err := c.client.ListStores(c.ctx, &aiquery.ListStores{})
    if err != nil {
        return err
    }
    for _, s := range resp.Stores {
        // Each AIStoreInfo includes Name, QueryModel, IndexModel, EmbeddingSize,
        // PredicateIndices ([]string), Dimension (uint32), and optional DbInfo (*db.server.StoreInfo).
        fmt.Printf("Store: %s, QueryModel: %v, IndexModel: %v, EmbeddingSize: %d, Dimension: %d, PredicateIndices: %v, DbInfo: %v\n",
            s.Name, s.QueryModel, s.IndexModel, s.EmbeddingSize, s.Dimension, s.PredicateIndices, s.DbInfo)
    }
    return nil
}

Get Store

func (c *ExampleAIClient) exampleGetStoreAI(ctx context.Context) error {
    resp, err := c.client.GetStore(c.ctx, &aiquery.GetStore{Store: "ai_store"})
    if err != nil { return err }
    // resp is a *AIStoreInfo with Name, QueryModel, IndexModel, EmbeddingSize,
    // PredicateIndices ([]string), Dimension (uint32), and optional DbInfo (*db.server.StoreInfo).
    fmt.Printf("Store: %s, QueryModel: %v, IndexModel: %v, EmbeddingSize: %d, Dimension: %d, PredicateIndices: %v, DbInfo: %v\n",
        resp.Name, resp.QueryModel, resp.IndexModel, resp.EmbeddingSize, resp.Dimension, resp.PredicateIndices, resp.DbInfo)
    return nil
}

Create Store

func (c *ExampleAIClient) exampleCreateStoreAI(ctx context.Context) error {
    _, err := c.client.CreateStore(c.ctx, &aiquery.CreateStore{
        Store:      "ai_store",
        QueryModel: aimodel.AIModel_ALL_MINI_LM_L6_V2,
        IndexModel: aimodel.AIModel_ALL_MINI_LM_L6_V2,
    })
    return err
}

Set

func (c *ExampleAIClient) exampleSetAI(ctx context.Context) error {
    inputs := []*keyval.AiStoreEntry{
        {
            Key: &keyval.StoreInput{Value: &keyval.StoreInput_RawString{RawString: "X"}},
            Value: &keyval.StoreValue{Value: map[string]*metadata.MetadataValue{
                "f": {Value: &metadata.MetadataValue_RawString{RawString: "v"}},
            }},
        },
    }
    _, err := c.client.Set(c.ctx, &aiquery.Set{
        Store:            "ai_store",
        Inputs:           inputs,
        PreprocessAction: preprocess.PreprocessAction_NoPreprocessing,
    })
    return err
}

Get Sim N

func (c *ExampleAIClient) exampleGetSimNAI(ctx context.Context) error {
    resp, err := c.client.GetSimN(c.ctx, &aiquery.GetSimN{
        Store:       "ai_store",
        SearchInput: &keyval.StoreInput{Value: &keyval.StoreInput_RawString{RawString: "X"}},
        ClosestN:    3,
    })
    if err != nil {
        return err
    }
    fmt.Println("AI GetSimN:", resp.Entries)
    return nil
}

Get By Predicate

func (c *ExampleAIClient) exampleGetByPredicateAI(ctx context.Context) error {
    cond := &predicates.PredicateCondition{
        Kind: &predicates.PredicateCondition_Value{Value: &predicates.Predicate{
            Kind: &predicates.Predicate_Equals{Equals: &predicates.Equals{
                Key:   "f",
                Value: &metadata.MetadataValue{Value: &metadata.MetadataValue_RawString{RawString: "v"}},
            }},
        }},
    }
    resp, err := c.client.GetByPredicate(c.ctx, &aiquery.GetByPredicate{
        Store:     "ai_store",
        Condition: cond,
    })
    if err != nil {
        return err
    }
    fmt.Println("AI GetByPredicate:", resp.Entries)
    return nil
}

Create Predicate Index

func (c *ExampleAIClient) exampleCreatePredicateIndexAI(ctx context.Context) error {
    _, err := c.client.CreatePredicateIndex(c.ctx, &aiquery.CreatePredicateIndex{
        Store:      "ai_store",
        Predicates: []string{"f"},
    })
    return err
}

Drop Predicate Index

func (c *ExampleAIClient) exampleDropPredicateIndexAI(ctx context.Context) error {
    _, err := c.client.DropPredicateIndex(c.ctx, &aiquery.DropPredicateIndex{
        Store:           "ai_store",
        Predicates:      []string{"f"},
        ErrorIfNotExists: true,
    })
    return err
}

Delete Key

func (c *ExampleAIClient) exampleDeleteKeyAI(ctx context.Context) error {
    _, err := c.client.DeleteKey(c.ctx, &aiquery.DeleteKey{
        Store: "ai_store",
        Keys:  []*keyval.StoreInput{{Value: &keyval.StoreInput_RawString{RawString: "X"}}},
    })
    return err
}

Drop Store

func (c *ExampleAIClient) exampleDropStoreAI(ctx context.Context) error {
    _, err := c.client.DropStore(c.ctx, &aiquery.DropStore{
        Store:           "ai_store",
        ErrorIfNotExists: true,
    })
    return err
}

Create Non Linear Algorithm Index

import (
    nonlinear "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/algorithm/nonlinear"
)

func (c *ExampleAIClient) exampleCreateNonLinearIndexAI(ctx context.Context) error {
    // Create a KDTree index
    _, err := c.client.CreateNonLinearAlgorithmIndex(c.ctx, &aiquery.CreateNonLinearAlgorithmIndex{
        Store: "ai_store",
        NonLinearIndices: []*nonlinear.NonLinearIndex{
            {Index: &nonlinear.NonLinearIndex_Kdtree{Kdtree: &nonlinear.KDTreeConfig{}}},
        },
    })
    if err != nil { return err }

    // Or create an HNSW index (with optional config)
    _, err = c.client.CreateNonLinearAlgorithmIndex(c.ctx, &aiquery.CreateNonLinearAlgorithmIndex{
        Store: "ai_store",
        NonLinearIndices: []*nonlinear.NonLinearIndex{
            {Index: &nonlinear.NonLinearIndex_Hnsw{Hnsw: &nonlinear.HNSWConfig{}}},
        },
    })
    return err
}

Drop Non Linear Algorithm Index

func (c *ExampleAIClient) exampleDropNonLinearIndexAI(ctx context.Context) error {
    _, err := c.client.DropNonLinearAlgorithmIndex(c.ctx, &aiquery.DropNonLinearAlgorithmIndex{
        Store:            "ai_store",
        NonLinearIndices: []nonlinear.NonLinearAlgorithm{nonlinear.NonLinearAlgorithm_KDTree},
        ErrorIfNotExists: true,
    })
    return err
}

Bulk Requests

Use pipeline to combine multiple calls:

DB Pipeline

import (
    "context"
    "fmt"
    "time"
    "google.golang.org/grpc/credentials/insecure"
    "google.golang.org/grpc"


    pipeline "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/db/pipeline"
    dbquery  "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/db/query"
    dbsvc    "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/services/db_service"
    keyval   "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/keyval"
)

const ServerAddr = "127.0.0.1:1369"

func examplePipelineDB() error {
    c.ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    conn, err := grpc.DialContext(c.ctx, proc.ServerAddr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
    if err != nil {
        return fmt.Errorf("failed to dial DB server %q: %w", ServerAddr, err)
    }
    defer conn.Close()

    client := dbsvc.NewDBServiceClient(conn)

    // Build pipeline queries
    setQ := &pipeline.DBQuery{Query: &pipeline.DBQuery_Set{Set: &dbquery.Set{
        Store:  "my_store",
        Inputs: []*keyval.DbStoreEntry{/* ... */},
    }}}
    getQ := &pipeline.DBQuery{Query: &pipeline.DBQuery_GetKey{GetKey: &dbquery.GetKey{
        Store: "my_store",
        Keys:  []*keyval.StoreKey{/* ... */},
    }}}

    // Execute pipeline
    req := &pipeline.DBRequestPipeline{Queries: []*pipeline.DBQuery{setQ, getQ}}
    resp, err := client.Pipeline(c.ctx, req)
    if err != nil {
        return err
    }
    fmt.Println("Pipeline responses:", resp.Responses)
    return nil
}

AI Pipeline

import (
    "context"
    "fmt"
    "time"
    "google.golang.org/grpc/credentials/insecure"
    "google.golang.org/grpc"

    pipeline "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/ai/pipeline"
    aisvc    "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/services/ai_service"
    aiquery  "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/ai/query"
    keyval   "github.com/deven96/ahnlich/sdk/ahnlich-client-go/grpc/keyval"
)

const ServerAddr = "127.0.0.1:1370"

func examplePipelineAI() error {
    c.ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    conn, err := grpc.DialContext(c.ctx, proc.ServerAddr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
    if err != nil {
        return fmt.Errorf("failed to dial AI server %q: %w", ServerAddr, err)
    }
    defer conn.Close()

    client := aisvc.NewAIServiceClient(conn)

    // Build pipeline queries
    setQ := &pipeline.AIQuery{Query: &pipeline.AIQuery_Set{Set: &aiquery.Set{
        Store:  "ai_store",
        Inputs: []*keyval.AiStoreEntry{/* ... */},
    }}}
    simQ := &pipeline.AIQuery{Query: &pipeline.AIQuery_GetSimN{GetSimN: &aiquery.GetSimN{
        Store:       "ai_store",
        SearchInput: &keyval.StoreInput{Value: &keyval.StoreInput_RawString{RawString: "X"}},
        ClosestN:    3,
    }}}

    // Execute pipeline
    req := &pipeline.AIRequestPipeline{Queries: []*pipeline.AIQuery{setQ, simQ}}
    resp, err := client.Pipeline(c.ctx, req)
    if err != nil {
        return err
    }
    fmt.Println("AI pipeline responses:", resp.Responses)
    return nil
}

Development & Testing

make install-dependencies
make generate    # regenerate Go protobuf code from proto definitions (requires buf)
make format
make test        # sequential
make lint-check

Deploy to GitHub Releases

  • Bump version in go.mod
  • Create a git tag vX.Y.Z
  • Push tag to trigger CI/CD release

Type Meanings

  • StoreKey: []float32 for DB, StoreInput for AI
  • StoreValue: map of metadata fields
  • Predicates: filter conditions for stored values
  • Pipeline: batch RPC builder

Change Log

Version Description
0.1.0 Initial Go SDK release
0.2.0 HNSW + AI Model Support