Skip to content

Commit

Permalink
added a distroless build, made sure milla works with gvisor's runsc a…
Browse files Browse the repository at this point in the history
…s the runtime
  • Loading branch information
terminaldweller committed May 13, 2024
1 parent 7b66051 commit 2c02180
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 66 deletions.
10 changes: 10 additions & 0 deletions Dockerfile_distroless
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM golang:1.21 as builder
WORKDIR /milla
COPY go.sum go.mod /milla/
RUN go mod download
COPY *.go /milla/
RUN CGO_ENABLED=0 go build

FROM gcr.io/distroless/static-debian12
COPY --from=builder /milla/milla "/usr/bin/milla"
ENTRYPOINT ["milla"]
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ The formatter to use. This tells chroma how to generate the color in the output.
- `terminal16m` for treucolor terminals
- `html` for HTML output

**_NOTE_**: please note that the terminal formatters will increase the size of the IRC event. Depending on the IRC server, this may or may not be a problem.

#### provider

Which LLM provider to use. The supported options are:
Expand All @@ -83,7 +85,7 @@ The system message to use for ollama.

#### clientCertPath

The path to the client certificate to use for SASL external authentication.
The path to the client certificate to use for client cert authentication.

#### serverPass

Expand Down Expand Up @@ -137,6 +139,8 @@ Whether to use TLS to connect to the IRC server. This option is provided to supp

#### disableSTSFallback

Disables the "fallback" to a non-TLS connection if the strict transport policy expires and the first attempt to reconnect back to the TLS version fails.

#### allowFlood

Disable [girc](https://github.com/lrstanley/girc)'s built-in flood protection.
Expand Down Expand Up @@ -183,9 +187,21 @@ Get the value of all config options.

Set a config option on the fly. Use the same name as the config file but capitalized.

## Proxy Support

milla will read and use the `ALL_PROXY` environment variable.
It is rather a non-standard way of using the env var but you define your socks5 proxies like so:

```
ALL_PROXY=127.0.0.1:9050
```

**_NOTE_**: the proxy is used for making calls to the LLMs, not for connecting to the IRC server.

## Deploy

An example docker compose file is provided in the repo under `docker-compose.yaml`.
milla can be used with [gvisor](https://gvisor.dev/)'s docker runtime, `runsc`.

```yaml
services:
Expand All @@ -197,26 +213,31 @@ services:
resources:
limits:
memory: 64M
user: ${UID}:${GID}
logging:
driver: "json-file"
options:
max-size: "100m"
networks:
- millanet
user: 1000:1000
restart: unless-stopped
command: ["--config", "/opt/milla/config.toml"]
volumes:
- ./config.toml:/opt/milla/config.toml
- /etc/ssl/certs:/etc/ssl/certs:ro
- ./config-gpt.toml:/opt/milla/config.toml
- /etc/localtime:/etc/localtime:ro
- /etc/resolv.conf:/etc/resolv.conf:ro
cap_drop:
- ALL
environment:
- SERVER_DEPLOYMENT_TYPE=deployment
runtime: runsc
networks:
millanet:
driver: bridge
```
The env vars `UID`and `GID`need to be defined or they can replaces by your host user's uid and gid.<br/>

As a convinience, there is a a [distroless](https://github.com/GoogleContainerTools/distroless) dockerfile, `Dockerfile_distroless` also provided.

## Thanks

- [girc](https://github.com/lrstanley/girc)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/google/generative-ai-go v0.11.2
github.com/lrstanley/girc v0.0.0-20240125042120-9add3166e52e
github.com/sashabaranov/go-openai v1.19.3
golang.org/x/net v0.24.0
google.golang.org/api v0.176.1
)

Expand Down Expand Up @@ -35,7 +36,6 @@ require (
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/oauth2 v0.19.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
Expand Down
113 changes: 54 additions & 59 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (
"fmt"
"log"
"net/http"
"net/url"
"os"
"reflect"
"regexp"
"strconv"
"strings"
"time"
Expand All @@ -22,6 +22,7 @@ import (
"github.com/google/generative-ai-go/genai"
"github.com/lrstanley/girc"
openai "github.com/sashabaranov/go-openai"
"golang.org/x/net/proxy"
"google.golang.org/api/option"
)

Expand Down Expand Up @@ -77,7 +78,6 @@ func NewTomlConfig() *TomlConfig {
ChromaStyle: "rose-pine-moon",
ChromaFormatter: "noop",
Provider: "ollama",
ClientCertPath: "milla.pem",
Temp: 0.5, //nolint:gomnd
RequestTimeout: 10, //nolint:gomnd
MillaReconnectDelay: 30, //nolint:gomnd
Expand Down Expand Up @@ -137,26 +137,26 @@ func returnGeminiResponse(resp *genai.GenerateContentResponse) string {
return result
}

// func extractLast256ColorEscapeCode(str string) (string, error) {
// pattern256F := `\033\[38;5;(\d+)m`
// // pattern256B := `\033\[48;5;(\d+)m`
// // pattern16mF := `\033\[38;2;(\d+);(\d+);(\d+)m`
// // pattern16mB := `\033\[48;2;(\d+);(\d+);(\d+)m`
func extractLast256ColorEscapeCode(str string) (string, error) {
pattern256F := `\033\[38;5;(\d+)m`
// pattern256B := `\033\[48;5;(\d+)m`
// pattern16mF := `\033\[38;2;(\d+);(\d+);(\d+)m`
// pattern16mB := `\033\[48;2;(\d+);(\d+);(\d+)m`

// r, err := regexp.Compile(pattern256F)
// if err != nil {
// return "", fmt.Errorf("failed to compile regular expression: %w", err)
// }
r, err := regexp.Compile(pattern256F)
if err != nil {
return "", fmt.Errorf("failed to compile regular expression: %w", err)
}

// matches := r.FindAllStringSubmatch(str, -1)
// if len(matches) == 0 {
// return "", nil
// }
matches := r.FindAllStringSubmatch(str, -1)
if len(matches) == 0 {
return "", nil
}

// lastMatch := matches[len(matches)-1]
lastMatch := matches[len(matches)-1]

// return lastMatch[1], nil
// }
return lastMatch[1], nil
}

func chunker(inputString string, chromaFormatter string) []string {
chunks := strings.Split(inputString, "\n")
Expand All @@ -169,17 +169,16 @@ func chunker(inputString string, chromaFormatter string) []string {
case "terminal16":
fallthrough
case "terminal256":
// for count, chunk := range chunks {
// lastColorCode, err := extractLast256ColorEscapeCode(chunk)
// if err != nil {
// continue
// }
for count, chunk := range chunks {
lastColorCode, err := extractLast256ColorEscapeCode(chunk)
if err != nil {
continue
}

// if count <= len(chunks)-2 {
// chunks[count+1] = fmt.Sprintf("\033[38;5;%sm", lastColorCode) + chunks[count+1]
// }
// }
fallthrough
if count <= len(chunks)-2 {
chunks[count+1] = fmt.Sprintf("\033[38;5;%sm", lastColorCode) + chunks[count+1]
}
}
case "terminal16m":
fallthrough
default:
Expand Down Expand Up @@ -384,20 +383,14 @@ func ollamaHandler(

request.Header.Set("Content-Type", "application/json")

httpClient := http.Client{}
allProxy := os.Getenv("ALL_PROXY")
if allProxy != "" {
proxyURL, err := url.Parse(allProxy)
if err != nil {
client.Cmd.ReplyTo(event, fmt.Sprintf("error: %s", err.Error()))
var httpClient http.Client

return
}
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}
dialer := proxy.FromEnvironment()

httpClient.Transport = transport
httpClient = http.Client{
Transport: &http.Transport{
Dial: dialer.Dial,
},
}

response, err := httpClient.Do(request)
Expand Down Expand Up @@ -564,24 +557,19 @@ func chatGPTHandler(
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(appConfig.RequestTimeout)*time.Second)
defer cancel()

allProxy := os.Getenv("ALL_PROXY")
config := openai.DefaultConfig(appConfig.Apikey)
if allProxy != "" {
proxyURL, err := url.Parse(allProxy)
if err != nil {
client.Cmd.ReplyTo(event, fmt.Sprintf("error: %s", err.Error()))
var httpClient http.Client

return
}
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}
dialer := proxy.FromEnvironment()

config.HTTPClient = &http.Client{
Transport: transport,
}
httpClient = http.Client{
Transport: &http.Transport{
Dial: dialer.Dial,
},
}

config := openai.DefaultConfig(appConfig.Apikey)
config.HTTPClient = &httpClient

gptClient := openai.NewClientWithConfig(config)

*gptMemory = append(*gptMemory, openai.ChatCompletionMessage{
Expand Down Expand Up @@ -645,7 +633,7 @@ func runIRC(appConfig TomlConfig, ircChan chan *girc.Client) {
DisableSTSFallback: appConfig.DisableSTSFallback,
GlobalFormat: true,
TLSConfig: &tls.Config{
InsecureSkipVerify: appConfig.SkipTLSVerify,
InsecureSkipVerify: appConfig.SkipTLSVerify, // #nosec G402
ServerName: appConfig.IrcServer,
},
})
Expand Down Expand Up @@ -676,9 +664,16 @@ func runIRC(appConfig TomlConfig, ircChan chan *girc.Client) {
}
}

// if appConfig.EnableSasl && appConfig.ClientCertPath != "" {
// // TODO - add client cert support
// }
if appConfig.EnableSasl && appConfig.ClientCertPath != "" {
cert, err := tls.LoadX509KeyPair(appConfig.ClientCertPath, appConfig.ClientCertPath)
if err != nil {
log.Println("invalid client certificate.")

return
}

irc.Config.TLSConfig.Certificates = []tls.Certificate{cert}
}

irc.Handlers.AddBg(girc.CONNECTED, func(c *girc.Client, e girc.Event) {
for _, channel := range appConfig.IrcChannels {
Expand All @@ -700,7 +695,7 @@ func runIRC(appConfig TomlConfig, ircChan chan *girc.Client) {
for {
if err := irc.Connect(); err != nil {
log.Println(err)
log.Println("reconnecting in" + strconv.Itoa(appConfig.MillaReconnectDelay))
log.Println("reconnecting in " + strconv.Itoa(appConfig.MillaReconnectDelay))
time.Sleep(time.Duration(appConfig.MillaReconnectDelay) * time.Second)
} else {
return
Expand Down

0 comments on commit 2c02180

Please sign in to comment.