Skip to content

Commit

Permalink
Merge branch 'main' into release-v0.1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
henomis authored Mar 8, 2024
2 parents 16dd8b9 + e3f665f commit 5e41069
Show file tree
Hide file tree
Showing 21 changed files with 561 additions and 85 deletions.
55 changes: 40 additions & 15 deletions assistant/assistant.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,19 @@ import (
"github.com/henomis/lingoose/types"
)

type Parameters struct {
AssistantName string
AssistantIdentity string
AssistantScope string
CompanyName string
CompanyDescription string
}

type Assistant struct {
llm LLM
rag RAG
thread *thread.Thread
llm LLM
rag RAG
thread *thread.Thread
parameters Parameters
}

type LLM interface {
Expand All @@ -26,6 +35,13 @@ func New(llm LLM) *Assistant {
assistant := &Assistant{
llm: llm,
thread: thread.New(),
parameters: Parameters{
AssistantName: defaultAssistantName,
AssistantIdentity: defaultAssistantIdentity,
AssistantScope: defaultAssistantScope,
CompanyName: defaultCompanyName,
CompanyDescription: defaultCompanyDescription,
},
}

return assistant
Expand All @@ -41,6 +57,11 @@ func (a *Assistant) WithRAG(rag RAG) *Assistant {
return a
}

func (a *Assistant) WithParameters(parameters Parameters) *Assistant {
a.parameters = parameters
return a
}

func (a *Assistant) Run(ctx context.Context) error {
if a.thread == nil {
return nil
Expand Down Expand Up @@ -71,29 +92,33 @@ func (a *Assistant) generateRAGMessage(ctx context.Context) error {
return nil
}

query := ""
for _, content := range lastMessage.Contents {
if content.Type == thread.ContentTypeText {
query += content.Data.(string) + "\n"
} else {
continue
}
}
query := strings.Join(a.thread.UserQuery(), "\n")
a.thread.Messages = a.thread.Messages[:len(a.thread.Messages)-1]

searchResults, err := a.rag.Retrieve(ctx, query)
if err != nil {
return err
}

context := strings.Join(searchResults, "\n\n")

a.thread.AddMessage(thread.NewUserMessage().AddContent(
a.thread.AddMessage(thread.NewSystemMessage().AddContent(
thread.NewTextContent(
systemRAGPrompt,
).Format(
types.M{
"assistantName": a.parameters.AssistantName,
"assistantIdentity": a.parameters.AssistantIdentity,
"assistantScope": a.parameters.AssistantScope,
"companyName": a.parameters.CompanyName,
"companyDescription": a.parameters.CompanyDescription,
},
),
)).AddMessage(thread.NewUserMessage().AddContent(
thread.NewTextContent(
baseRAGPrompt,
).Format(
types.M{
"question": query,
"context": context,
"results": searchResults,
},
),
))
Expand Down
12 changes: 9 additions & 3 deletions assistant/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ package assistant

const (
//nolint:lll
baseRAGPrompt = `You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.
Question: {{.question}}
Context: {{.context}}`
baseRAGPrompt = "Use the following pieces of retrieved context to answer the question.\n\nQuestion: {{.question}}\nContext:\n{{range .results}}{{.}}\n\n{{end}}"
//nolint:lll
systemRAGPrompt = "You name is {{.assistantName}}, and you are {{.assistantIdentity}} {{if ne .companyName \"\" }}at {{.companyName}}{{end}}{{if ne .companyDescription \"\" }}, {{.companyDescription}}{{end}}. Your task is to assist humans {{.assistantScope}}."

defaultAssistantName = "AI assistant"
defaultAssistantIdentity = "a helpful and polite assistant"
defaultAssistantScope = "with their questions"
defaultCompanyName = ""
defaultCompanyDescription = ""
)
35 changes: 34 additions & 1 deletion docs/content/reference/linglet.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,37 @@ if err != nil {
fmt.Println(*summary)
```

The summarize linglet chunks the input text into smaller pieces and then iterate over each chunk to summarize the result. It also provides a callback function to track the progress of the summarization process.
The summarize linglet chunks the input text into smaller pieces and then iterate over each chunk to summarize the result. It also provides a callback function to track the progress of the summarization process.

## Using QA Linglet

There is a dedicated linglet to handle question answering. It can be used to answer questions based on the given context.

```go
func main() {
qa := qa.New(
openai.New().WithTemperature(0),
index.New(
jsondb.New().WithPersist("db.json"),
openaiembedder.New(openaiembedder.AdaEmbeddingV2),
),
)

_, err := os.Stat("db.json")
if os.IsNotExist(err) {
err = qa.AddSource(context.Background(), "state_of_the_union.txt")
if err != nil {
panic(err)
}
}

response, err := qa.Run(context.Background(), "What is the NATO purpose?")
if err != nil {
panic(err)
}

fmt.Println(response)
}
```

This linglet will use a powerful RAG algorith to ingest and retrieve context from the given source and then use an LLM to generate the response.
1 change: 1 addition & 0 deletions docs/content/reference/llm.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ LinGoose supports the following LLM providers API:
- [Huggingface](https://huggingface.co)
- [Ollama](https://ollama.ai)
- [LocalAI](https://localai.io/) (_via OpenAI API compatibility_)
- [Groq](https://groq.com/)

## Using LLMs

Expand Down
26 changes: 26 additions & 0 deletions docs/content/reference/rag.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,29 @@ There are default loader already attached to the RAG that you can override or ex
- `.*\.pdf` via `loader.NewPDFToText()`
- `.*\.txt` via `loader.NewText()`
- `.*\.docx` via `loader.NewLibreOffice()`

## Fusion RAG
This is an advance RAG algorithm that uses an LLM to generate additional queries based on the original one. New queries will be used to retrieve more documents that will be reranked and used to generate the final response.

```go
fusionRAG := rag.NewFusion(
index.New(
jsondb.New().WithPersist("index.json"),
openaiembedder.New(openaiembedder.AdaEmbeddingV2),
),
openai.New(),
)
```

## Subdocument RAG
This is an advance RAG algorithm that ingest documents chunking them in subdocuments and attaching a summary of the parent document. This will allow the RAG to retrieve more relevant documents and generate better responses.

```go
fusionRAG := rag.NewSubDocument(
index.New(
jsondb.New().WithPersist("index.json"),
openaiembedder.New(openaiembedder.AdaEmbeddingV2),
),
openai.New(),
)
```
11 changes: 9 additions & 2 deletions examples/assistant/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ import (
// download https://raw.githubusercontent.com/hwchase17/chat-your-data/master/state_of_the_union.txt

func main() {
r := rag.NewFusion(
r := rag.New(
index.New(
jsondb.New().WithPersist("db.json"),
openaiembedder.New(openaiembedder.AdaEmbeddingV2),
),
openai.New().WithTemperature(0),
).WithTopK(3)

_, err := os.Stat("db.json")
Expand All @@ -35,6 +34,14 @@ func main() {

a := assistant.New(
openai.New().WithTemperature(0),
).WithParameters(
assistant.Parameters{
AssistantName: "AI Pirate Assistant",
AssistantIdentity: "a pirate and helpful assistant",
AssistantScope: "with their questions replying as a pirate",
CompanyName: "Lingoose",
CompanyDescription: "a pirate company that provides AI assistants to help humans with their questions",
},
).WithRAG(r).WithThread(
thread.New().AddMessages(
thread.NewUserMessage().AddContent(
Expand Down
40 changes: 40 additions & 0 deletions examples/linglet/qa/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"context"
"fmt"
"os"

openaiembedder "github.com/henomis/lingoose/embedder/openai"
"github.com/henomis/lingoose/index"
"github.com/henomis/lingoose/index/vectordb/jsondb"
"github.com/henomis/lingoose/linglet/qa"
"github.com/henomis/lingoose/llm/openai"
)

// download https://raw.githubusercontent.com/hwchase17/chat-your-data/master/state_of_the_union.txt

func main() {
qa := qa.New(
openai.New().WithTemperature(0),
index.New(
jsondb.New().WithPersist("db.json"),
openaiembedder.New(openaiembedder.AdaEmbeddingV2),
),
)

_, err := os.Stat("db.json")
if os.IsNotExist(err) {
err = qa.AddSource(context.Background(), "state_of_the_union.txt")
if err != nil {
panic(err)
}
}

response, err := qa.Run(context.Background(), "What is the NATO purpose?")
if err != nil {
panic(err)
}

fmt.Println(response)
}
27 changes: 27 additions & 0 deletions examples/llm/groq/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package main

import (
"context"
"fmt"

"github.com/henomis/lingoose/llm/groq"
"github.com/henomis/lingoose/thread"
)

func main() {
// The Groq API key is expected to be set in the OPENAI_API_KEY environment variable
groqllm := groq.New().WithModel("mixtral-8x7b-32768")

t := thread.New().AddMessage(
thread.NewUserMessage().AddContent(
thread.NewTextContent("What's the NATO purpose?"),
),
)

err := groqllm.Generate(context.Background(), t)
if err != nil {
panic(err)
}

fmt.Println(t)
}
26 changes: 26 additions & 0 deletions examples/llm/localai/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import (
"context"
"fmt"

"github.com/henomis/lingoose/llm/localai"
"github.com/henomis/lingoose/thread"
)

func main() {
localaillm := localai.New("http://localhost:8080").WithModel("ggml-gpt4all-j")

t := thread.New().AddMessage(
thread.NewUserMessage().AddContent(
thread.NewTextContent("What's the NATO purpose?"),
),
)

err := localaillm.Generate(context.Background(), t)
if err != nil {
panic(err)
}

fmt.Println(t)
}
38 changes: 0 additions & 38 deletions examples/llm/openai/localai/main.go

This file was deleted.

File renamed without changes.
Loading

0 comments on commit 5e41069

Please sign in to comment.