diff --git a/supporting-blog-content/ollama-and-go-for-rag/elasticsearch/elasticsearch.go b/supporting-blog-content/ollama-and-go-for-rag/elasticsearch/elasticsearch.go new file mode 100644 index 00000000..55131bf0 --- /dev/null +++ b/supporting-blog-content/ollama-and-go-for-rag/elasticsearch/elasticsearch.go @@ -0,0 +1,76 @@ +package elasticsearch + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + "github.com/elastic/go-elasticsearch/v8" + "github.com/elastic/go-elasticsearch/v8/typedapi/types" +) + +// Initializing elasticsearch client + +func EsClient() (*elasticsearch.TypedClient, error) { + var cloudID = "" // your Elastic Cloud ID Here + var apiKey = "" // your Elastic ApiKey Here + + es, err := elasticsearch.NewTypedClient(elasticsearch.Config{ + CloudID: cloudID, + APIKey: apiKey, + }) + + if err != nil { + return nil, fmt.Errorf("unable to connect: %w", err) + } + return es, nil +} + +// Searching for documents and building the context +func SemanticRetriever(client *elasticsearch.TypedClient, query string, size int) (string, error) { + // Perform the semantic search + res, err := client.Search(). + Index("rag-ollama"). + Query(&types.Query{ + Semantic: &types.SemanticQuery{ + Field: "semantic_field", + Query: query, + }, + }). + Size(size). + Do(context.Background()) + + if err != nil { + return "", fmt.Errorf("semantic search failed: %w", err) + } + + // Prepare to format the results + var output strings.Builder + output.WriteString("Documents found\n\n") + + // Iterate through the search hits + for i, hit := range res.Hits.Hits { + // Define a struct to unmarshal each document + var doc struct { + Title string `json:"title"` + Content string `json:"content"` + } + + // Unmarshal the document source into our struct + if err := json.Unmarshal(hit.Source_, &doc); err != nil { + return "", fmt.Errorf("failed to unmarshal document %d: %w", i, err) + } + + // Append the formatted document to our output + output.WriteString(fmt.Sprintf("Title\n%s\n\nContent\n%s\n", doc.Title, doc.Content)) + + // Add a separator between documents, except for the last one + if i < len(res.Hits.Hits)-1 { + output.WriteString("\n-----\n\n") + } + } + + // Return the formatted output as a string + return output.String(), nil +} diff --git a/supporting-blog-content/ollama-and-go-for-rag/go.mod b/supporting-blog-content/ollama-and-go-for-rag/go.mod new file mode 100644 index 00000000..21bc0f51 --- /dev/null +++ b/supporting-blog-content/ollama-and-go-for-rag/go.mod @@ -0,0 +1,14 @@ +module ollama-rag + +go 1.23.2 + +require ( + github.com/elastic/elastic-transport-go/v8 v8.6.0 // indirect + github.com/elastic/go-elasticsearch/v8 v8.15.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/parakeet-nest/parakeet v0.2.1 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect +) diff --git a/supporting-blog-content/ollama-and-go-for-rag/go.sum b/supporting-blog-content/ollama-and-go-for-rag/go.sum new file mode 100644 index 00000000..d7c003d8 --- /dev/null +++ b/supporting-blog-content/ollama-and-go-for-rag/go.sum @@ -0,0 +1,17 @@ +github.com/elastic/elastic-transport-go/v8 v8.6.0 h1:Y2S/FBjx1LlCv5m6pWAF2kDJAHoSjSRSJCApolgfthA= +github.com/elastic/elastic-transport-go/v8 v8.6.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk= +github.com/elastic/go-elasticsearch/v8 v8.15.0 h1:IZyJhe7t7WI3NEFdcHnf6IJXqpRf+8S8QWLtZYYyBYk= +github.com/elastic/go-elasticsearch/v8 v8.15.0/go.mod h1:HCON3zj4btpqs2N1jjsAy4a/fiAul+YBP00mBH4xik8= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/parakeet-nest/parakeet v0.2.1 h1:JVw73nDhQUtl+FN7X4s23oqqSv6Wg/+KEaQhhpxzRGg= +github.com/parakeet-nest/parakeet v0.2.1/go.mod h1:8M3ApjoLs/+tFuAqCxE2g9iSAODDT3OoW/Z6c5z++gw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= diff --git a/supporting-blog-content/ollama-and-go-for-rag/main.go b/supporting-blog-content/ollama-and-go-for-rag/main.go new file mode 100644 index 00000000..a8e08052 --- /dev/null +++ b/supporting-blog-content/ollama-and-go-for-rag/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "fmt" + "log" + "ollama-rag/elasticsearch" + + "github.com/parakeet-nest/parakeet/completion" + "github.com/parakeet-nest/parakeet/enums/option" + "github.com/parakeet-nest/parakeet/llm" +) + +func main() { + + ollamaUrl := "http://localhost:11434" + chatModel := "llama3.2:latest" + question := `Summarize document: JAK Inhibitors vs. Monoclonal Antibodies in Rheumatoid Arthritis Treatment` + size := 3 + + esClient, err := elasticsearch.EsClient() + + if err != nil { + log.Fatalln("😡:", err) + } + + // Retrieve documents from semantic query to build context + documentsContent, nil := elasticsearch.SemanticRetriever(esClient, question, size) + + systemContent := `You are a helpful medical assistant. Only answer the questions based on found documents. + Add references to the base document titles and be succint in your answers.` + + options := llm.SetOptions(map[string]interface{}{ + option.Temperature: 0.0, + }) + + queryChat := llm.Query{ + Model: chatModel, + Messages: []llm.Message{ + {Role: "system", Content: systemContent}, + {Role: "system", Content: documentsContent}, + {Role: "user", Content: question}, + }, + Options: options, + } + + fmt.Println() + fmt.Println("🤖 answer:") + + // Answer the question + _, err = completion.ChatStream(ollamaUrl, queryChat, + func(answer llm.Answer) error { + fmt.Print(answer.Message.Content) + return nil + }) + if err != nil { + log.Fatal("😡:", err) + } + + fmt.Println() +}