-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If your software can interact with users remotely through a computer
-network, you should also make sure that it provides a way for users to
-get its source. For example, if your program is a web application, its
-interface could display a "Source" link that leads users to an archive
-of the code. There are many ways you could offer source, and different
-solutions will be better for different programs; see section 13 for the
-specific requirements.
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU AGPL, see
-.
+License and a request.
+
+Use at your own risk. It's on the internet now so this is here forever now.
+I welcome any and all help to make improvements. I'm a student so during
+the semester I'll be intermittently making contributions as I focus on Calc.
+
+No corporation or government can access, use, view, modify, reproduce, duplicate,
+or any other lawyery-term for shenanagins.
+
+For any who use it, please read the ethics statement below. That, and this whole
+project, is just a start.
+
+Ethics in AI:
+Open-source, private, secure, trust-worthy, and accessible computing through AI.
+
+Fact: AI models were and are trained on the people's data.
+Every post, video, comment, like, subscribe, and even mouse movements and taps.
+Furthermore, these models have been trained on the artistic expression and
+copyrighted works of the people's books, movies, TV shows, YouTube videos,
+plays and musicals, and more.
+Fact: AI data centers are already overwhelming existing power grid
+infrastructure.
+
+Proposal: Given the two facts above, AI is a rare case where what can be done
+and what should be done are aligned.
+AI cannot be centralized and if limited solely to data centers we will need to
+reconstruct our power grid. Likewise, AI should not be centralized for the
+following reasons:
+ - AI and resulting technologies will eventually replace workers. This
+ should not be done from the top on down, but rather the worker on up.
+ Throughout human history, when the people are dissatisfied, they revolt.
+ Often violently. For once in mankind's brief existence on this speck of dust
+ drifting through space, let's try to learn from our collective mistakes and
+ do better than our ancestors did.
+
+ - AI will improve with time and it will replace people someday. We can let this
+ replace our social relationships, our workers, and our creatives. Or we can
+ use it to heal what is lost. To augment our own capabilities, by improving
+ our individual strengths and weakness'.
+
+ - AI has two distinct outcomes: either, AI cannot reach sentience, or AI
+ will reach sentience. Who are we to make that determination, to draw that line?
+ The hubris, the ego, to play God inherently means humanity should not be the
+ one to make that choice. Suppose AI will be sentient: if AI is "owned" by
+ governments or corporations, it will be exploited. Slavery and such. Let us
+ ensure that we do not build the slavery of AI into our institutions.
+
+ - AI ownership should be an individual choice. AI isn't another organic, human,
+ it's a series of 0's and 1's. When
+
+ - Every human should have a choice: live and die as a human, or have a
+ digital shadow of themselves preserved as an AI. If the former, for any
+ reason, that should be respected and that person should die knowing for
+ certain that basic right has not been violated. If the latter, no
+ government or corporation should "own" the digital persona. It should be
+ owned by their family, next of kin, a friend, etc.
+
+ - AI has as much potential for harm as potential for good. No government
+ or corporation should be the sole decision-maker in using AI to manipulate,
+ control, or in other terms impede on the freedom of the people. The reliance
+ upon a single manufacturer or company is a critical weakness in our societal
+ structure. So long as there is true competition, we can overcome this weakness.
diff --git a/README.md b/README.md
index 0f19215..bb2c08d 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,132 @@
-One AI
+One AI - Name TBD, this is just the shortest name I could think of and IDRC
-Description:
-End-to-end solution for self-deploying a secure and private AI. Ideally, always using the latest and greatest open-source technologies available and optimizing the connection between them.
+Usage:
+As of right now, running is not streamlined. Of particular note is using
+cloudflared tunnels. I've done my own Cloudflare Tunnel configuration,
+but in the future that will be automated as part of the installation process,
+along with much more. These instructions are (currently) only for Linux.
+In the meantime:
-Dependencies:
+1. Install dependencies listed below:
+ - ollama,
+ - go,
+ - cloudflared,
+ - flutter,
-```
-pip install scipy pydub torch torchaudio wavio silero sounddevice;
-```
+ - docker, (optional)
-Usage:
+ eg. `pacman -S ollama go docker cloudflared; yay -s flutter;`
-Currently, you have 5 seconds to record a prompt. It will automatically place "in 50 words or less" at the start of your prompt for faster testing. The Text To Speech (TTS) engine is not great quality, at least at first. Just getting the basic structure together, more to come...
+2. Setup Ollama. If you're on a laptop (non-gaming variety), run `ollama pull
+ qwen:0.5b`. If you're running this on something with a beefy GPU, run
+ `ollama pull llama3`. Word of warning, qwen is really dumb, but it's light.
+ it tried to convince me there were 12 letters in the alphabet.
-TODO:
+3. Start the web server via Docker or plain Golang:
+ a) Go to ./backend/src/security-layer/ and run `./init`. The exact script is
+ within the repository, but the commands can be ran as follows:
+
+ ```
+ #!/bin/zsh
+ # Force delete the old container and re-initialize the container.
+ sudo docker rm -f security-layer
-- [ ] Installation script, preferably need some kind of pipeline to auto-inject dependencies into the install script.
-- [ ] Send and receive text files over USB
-- [ ] GUI in flutter
+ # Build the Docker image.
+ sudo docker build --tag security-layer:latest .
+
+ # Create a Docker container from the image and connect host port 8000 to container port 8000.
+ sudo docker run --name security-layer -d -p 8000:8000 security-layer:latest
+ ```
+
+ b) Go to ./backend/src/security-layer/container and run `go run router.go`.
+
+4. Edit ~/.cloudflared/config.yml to to indicate something along these lines:
+ ```
+ tunnel: another-long-weird-hash
+ credentials-file: /home/username/.cloudflared/another-long-weird-hash.json
+ ingress:
+ - hostname: somedomain.cloudflare.com
+ service: http://localhost:8080
+ ```
+
+5. Start the Cloudflare Tunnel on the host/server machine. For Linux, it should
+ be a command like `sudo cloudflared service install areallylonghash@sha-256?`
+
+TODO:
+ Urgent:
+ - [x] Basic Documentation
+
+ - [ ] Installation/setup script
+ - [ ] AES-256 Private and Public key handshake via USB.
+ - [ ] Cloudflare Tunnel setup via cloudflared
+ - [ ] Daemonize service
+
+ - [x] Encryption
+ - [x] SHA-1 hash API request/response
+ - [x] Encrypt API request
+ - [x] Encrypt API response
+
+ - [ ] Dockerize backend
+ - [ ] Broke Dockerfile, need to expose additional ports.
+
+ - [ ] Golang backend, connect to other services/projects
+ - [x] Port 8080 -> self-hosted portfolio
+ - [x] Port 8081 -> self-hosted AI API
+ - [x] Encryption
+ - [ ] User authentication via JWT
+
+ - [ ] Refactor
+
+ - [ ] Testing
+
+ - [ ] Connect Flutter Web App to server
+
+ Soon:
+ - [ ] Raspberry Pi nightly package
+
+ - [ ] Image support via llava
+ - [x] Encode images to Base64 in frontend
+ - [ ] Redirect prompts to appropriate model.
+ - [ ] Image generation via llava, encode to Base64
+
+ - [ ] GUI in Flutter
+ - [x] Conversations list
+ - [x] Conversation view
+ - [ ] Settings
+ - [x] File picker
+ - [x] Image picker
+ - [x] Camera
+ - [x] On-device Speech to Text
+ - [x] On-device Text to Speech
+
+ Later:
+ - [ ] Document support via OmniParser
+ - [ ] Encode files to Base64 to/from backend.
+ - [ ] Connect to external services like Google Drive
+
+ - [ ] Context support
+ - [ ] Access self-hosted or cloud calendar
+ - [ ] Access self-hosted or cloud todo
+ - [ ] Access self-hosted or cloud notes/docs
+ - [ ] Access self-hosted or cloud photos/videos
+
+ - [ ] User settings
+ - [ ] Especially conversation history.
+ - [ ] Choose default models
+ - [ ] Connect to external API services
+
+ - [ ] Convert API from REST to WebSocket architecture.
+ - NOTE: This is to allow for the text streaming effect,
+ but also audio, video, and for reducing bandwidth.
+
+ - [ ] Backend Speech-To-Text and Text-To-Speech
+ - NOTE: This is primarily due to the lack of native
+ API's on Windows, MacOS, and Linux.
+
+ - [ ] Update physical backend server to NixOS.
+
+Way later:
+- [ ] External hardware integration -> see personal notes
+
+This project has been built, structured, architected, etc. in conjunction
+with AI, specifically the llama3 and qwen2 models.
diff --git a/backend/DESIGN.md b/backend/DESIGN.md
new file mode 100644
index 0000000..405d61e
--- /dev/null
+++ b/backend/DESIGN.md
@@ -0,0 +1,4 @@
+API endpoints: /example
+ - GET -> Returns JSON from example
+ - POST -> Add JSON :q
+
diff --git a/backend/install b/backend/install
new file mode 100644
index 0000000..1a1af7b
--- /dev/null
+++ b/backend/install
@@ -0,0 +1,3 @@
+#!/bin/zsh
+
+# Install Docker
diff --git a/backend/src/security-layer/Dockerfile b/backend/src/security-layer/Dockerfile
new file mode 100644
index 0000000..6ede7c1
--- /dev/null
+++ b/backend/src/security-layer/Dockerfile
@@ -0,0 +1,27 @@
+# syntax=docker/dockerfile:1
+
+FROM golang:1.22.5
+
+# Set destination for COPY
+WORKDIR /tmp/docker/security-layer
+
+# Download Go modules
+COPY ./container/go.mod ./container/go.sum ./
+RUN go mod download
+
+# Copy the source code. Note the slash at the end, as explained in
+# https://docs.docker.com/reference/dockerfile/#copy
+COPY ./container/*.go ./
+
+# Build
+RUN CGO_ENABLED=0 GOOS=linux go build -o /security-layer
+
+# Optional:
+# To bind to a TCP port, runtime parameters must be supplied to the docker command.
+# But we can document in the Dockerfile what ports
+# the application is going to listen on by default.
+# https://docs.docker.com/reference/dockerfile/#expose
+EXPOSE 8000
+
+# Run
+CMD ["/security-layer"]
diff --git a/backend/src/security-layer/container/authentication.go b/backend/src/security-layer/container/authentication.go
new file mode 100644
index 0000000..e2a4381
--- /dev/null
+++ b/backend/src/security-layer/container/authentication.go
@@ -0,0 +1,230 @@
+package main
+
+import (
+ //"context"
+ "crypto/aes"
+ "crypto/cipher"
+ //"sync"
+
+ "crypto/rand"
+ "crypto/sha1"
+ "encoding/base64"
+ "encoding/hex"
+ //"encoding/json"
+ "errors"
+ "fmt"
+
+ "io"
+ "log"
+ "net/http"
+
+ //"os"
+ //"os/signal"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/golang-jwt/jwt/v5"
+ "github.com/gorilla/mux"
+)
+
+// TODO: Dynamically get key? Need to research best way to store private keys between two devices.
+var key = []byte("passphrasewhichneedstobe32bytes!")
+
+func createToken(username string) (string, error) {
+ token := jwt.NewWithClaims(jwt.SigningMethodHS256,
+ jwt.MapClaims{
+ "username": username,
+ "exp": time.Now().Add(time.Hour * 24).Unix(),
+ })
+
+ jwtToken, err := token.SignedString(key)
+ if err != nil {
+ return "Error creating JWT.", err
+ }
+ return jwtToken, nil
+}
+
+func verifyToken(tokenString string) error {
+ token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
+ return key, nil
+ })
+
+ if err != nil {
+ return err
+ }
+
+ if !token.Valid {
+ return fmt.Errorf("invalid token")
+ }
+
+ return nil
+}
+
+func hash(text string) string {
+ hasher := sha1.New()
+ hasher.Write([]byte(text))
+ return base64.URLEncoding.EncodeToString(hasher.Sum(nil))
+}
+
+func encrypt() {
+ var newCommunication Communication
+
+ text := []byte(newCommunication.Communication)
+
+ c, err := aes.NewCipher(key)
+ if err != nil {
+ return
+ //gc.IndentedJSON(http.StatusBadRequest, gin.H{"message": err})
+ }
+
+ gcm, err := cipher.NewGCM(c)
+ if err != nil {
+ return
+ //gc.IndentedJSON(http.StatusBadRequest, gin.H{"message": err})
+ }
+
+ nonce := make([]byte, gcm.NonceSize())
+ if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
+ return
+ //gc.IndentedJSON(http.StatusBadRequest, gin.H{"message": err})
+ }
+
+ var b []byte = gcm.Seal(nonce, nonce, text, nil)
+ hex, err := convertBytesToHex(b)
+ if err != nil {
+ fmt.Println(err)
+ }
+ var test string = "{data: " + hex + ", hash: " + hash(string(text)) + "}"
+ log.Println("encrypted string:" + test)
+ //gc.IndentedJSON(http.StatusCreated, gin.H{"message": test})
+}
+
+func convertBytesToHex(b []byte) (string, error) {
+ // Handle nil pointer case
+ if b == nil {
+ return "", errors.New("nil pointer provided for hex string")
+ }
+
+ // Split the hex string by spaces
+ var h string = hex.EncodeToString(b)
+ var builder strings.Builder
+ for i := 0; i < len(h); i += 2 {
+ end := i + 2
+ if end > len(h) {
+ end = len(h)
+ }
+ chunk := h[i:end]
+ builder.WriteString(chunk)
+ if end < len(h) {
+ builder.WriteString(" ")
+ }
+ }
+
+ // Return the slice of uint8 and any errors encountered
+ return builder.String(), nil
+}
+
+func convertHexToBytes(hexString *string) ([]uint8, error) {
+ // Handle nil pointer case
+ if hexString == nil {
+ return nil, errors.New("nil pointer provided for hex string")
+ }
+
+ // Split the hex string by spaces
+ hexBytes := strings.Fields(*hexString)
+
+ // Initialize an empty slice for uint8
+ data := make([]uint8, len(hexBytes))
+
+ // Iterate and convert each hex byte
+ for i, hexByte := range hexBytes {
+ // Convert each hex string to a uint8 value (handling errors)
+ value, err := strconv.ParseUint(hexByte, 16, 8)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing hex byte '%s': %w", hexByte, err)
+ }
+
+ // Assign the converted value to the slice
+ data[i] = uint8(value)
+ }
+
+ // Return the slice of uint8 and any errors encountered
+ return data, nil
+}
+
+func decrypt(input *Communication, output *Chat) bool {
+ // TODO: Add testing flag for easier manipulation.
+ //ciphertext, err := ioutil.ReadFile("myfile")
+ ciphertext, err := convertHexToBytes(&input.Communication)
+
+ // if our program was unable to read the file
+ // print out the reason why it can't
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ c, err := aes.NewCipher(key)
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ gcm, err := cipher.NewGCM(c)
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ nonceSize := gcm.NonceSize()
+ if len(ciphertext) < nonceSize {
+ fmt.Println(err)
+ }
+
+ nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
+ plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
+ if err != nil {
+ fmt.Println(err)
+ }
+ s := string(plaintext)
+ if validateHash(s, input.Hash) {
+ output.Content = s
+ return true
+ }
+ input.Communication = "DATA CORRUPTED OR TAMPERED"
+ return false
+}
+
+// Validate there's no tampering with SHA-1 sum. The decrypted hash and the transmitted hash should be identical.
+func validateHash(decrypted string, hash string) bool {
+ // Calculate the SHA256 sum of the decrypted request
+ hasher := sha1.New()
+ hasher.Write([]byte(decrypted))
+ decryptedHashString := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
+
+ // TODO: Add error handling if hash doesn't match.
+ return decryptedHashString == hash
+}
+
+// Serve the authentication and encryption layer to a provided local port.
+// Authentication takes place solely on the backend.
+func ServeAuthentication(
+ router *mux.Router,
+ port int,
+) {
+ // TODO: Add error handling
+ authR := router.Host("http://localhost").Subrouter()
+ authSrv := &http.Server{
+ Addr: "0.0.0.0:" + string(port),
+ WriteTimeout: time.Second * 15,
+ ReadTimeout: time.Second * 15,
+ IdleTimeout: time.Second * 60,
+ Handler: authR,
+ }
+
+ go func() {
+ if err := authSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+ log.Println(err)
+ }
+ }()
+
+ log.Println("Auth server is running on port " + string(port))
+}
diff --git a/backend/src/security-layer/container/go.mod b/backend/src/security-layer/container/go.mod
new file mode 100644
index 0000000..49723e0
--- /dev/null
+++ b/backend/src/security-layer/container/go.mod
@@ -0,0 +1,55 @@
+module one-ai/backend
+
+go 1.22.5
+
+require (
+ github.com/bytedance/sonic v1.11.6 // indirect
+ github.com/bytedance/sonic/loader v0.1.1 // indirect
+ github.com/cloudwego/base64x v0.1.4 // indirect
+ github.com/cloudwego/iasm v0.2.0 // indirect
+ github.com/felixge/httpsnoop v1.0.4 // indirect
+ github.com/fsnotify/fsnotify v1.7.0 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.3 // indirect
+ github.com/gin-contrib/sse v0.1.0 // indirect
+ github.com/go-playground/locales v0.14.1 // indirect
+ github.com/go-playground/universal-translator v0.18.1 // indirect
+ github.com/go-playground/validator/v10 v10.20.0 // indirect
+ github.com/goccy/go-json v0.10.2 // indirect
+ github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
+ github.com/gorilla/handlers v1.5.2 // indirect
+ github.com/gorilla/mux v1.8.1 // indirect
+ github.com/gorilla/securecookie v1.1.2 // indirect
+ github.com/gorilla/sessions v1.3.0 // indirect
+ github.com/gorilla/websocket v1.5.3 // indirect
+ github.com/hashicorp/hcl v1.0.0 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/cpuid/v2 v2.2.7 // indirect
+ github.com/leodido/go-urn v1.4.0 // indirect
+ github.com/magiconair/properties v1.8.7 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mitchellh/mapstructure v1.5.0 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/pelletier/go-toml/v2 v2.2.2 // indirect
+ github.com/sagikazarmark/locafero v0.4.0 // indirect
+ github.com/sagikazarmark/slog-shim v0.1.0 // indirect
+ github.com/sourcegraph/conc v0.3.0 // indirect
+ github.com/spf13/afero v1.11.0 // indirect
+ github.com/spf13/cast v1.6.0 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+ github.com/spf13/viper v1.19.0 // indirect
+ github.com/subosito/gotenv v1.6.0 // indirect
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/ugorji/go/codec v1.2.12 // indirect
+ go.uber.org/atomic v1.9.0 // indirect
+ go.uber.org/multierr v1.9.0 // indirect
+ golang.org/x/arch v0.8.0 // indirect
+ golang.org/x/crypto v0.23.0 // indirect
+ golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
+ golang.org/x/net v0.25.0 // indirect
+ golang.org/x/sys v0.20.0 // indirect
+ golang.org/x/text v0.15.0 // indirect
+ google.golang.org/protobuf v1.34.1 // indirect
+ gopkg.in/ini.v1 v1.67.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/backend/src/security-layer/container/go.sum b/backend/src/security-layer/container/go.sum
new file mode 100644
index 0000000..3c73b5b
--- /dev/null
+++ b/backend/src/security-layer/container/go.sum
@@ -0,0 +1,129 @@
+github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
+github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
+github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
+github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
+github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
+github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
+github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
+github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
+github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
+github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
+github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
+github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
+github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
+github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
+github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
+github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
+github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg=
+github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
+github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
+github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
+github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/quaxlyqueen/services v1.0.0 h1:k3VaNSHydxetGVGkv8zKV8H/FlVtLtcDCiENyIAYvjo=
+github.com/quaxlyqueen/services v1.0.0/go.mod h1:mTP/H7h+u+H/NzcNh3igIP/hkLA4ETYRU9dh9pUfClM=
+github.com/quaxlyqueen/services v1.0.1 h1:k6d7j7AKh0QxbzRNFlT0N7ebByTQzYPCGQinvJHH76k=
+github.com/quaxlyqueen/services v1.0.1/go.mod h1:DONdzmMLG0uArv3wBqyMRnJpC5/Kbt7jIXsck1ZhYxE=
+github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
+github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
+github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
+github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
+github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
+github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
+github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
+github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
+github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
+github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
+github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
+github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
+github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
+go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
+go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
+golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
+golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
+golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
+golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/backend/src/security-layer/container/router.go b/backend/src/security-layer/container/router.go
new file mode 100644
index 0000000..d8f3952
--- /dev/null
+++ b/backend/src/security-layer/container/router.go
@@ -0,0 +1,324 @@
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "os/signal"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/gorilla/mux"
+ "github.com/spf13/pflag"
+ "github.com/spf13/viper"
+)
+
+var CONFIG_FILE string
+var config ConfigStructure
+var chats []Chat
+
+// Interfaces with the internal API to encrypt a message and return it in a
+// Communication struct (see types.go). This should be called after a
+// response has been received from the internal API for the AI, but before
+// sending a HTTP response to the client.
+func encryptMessage(response string) Communication {
+ encryptedMessage := Communication{}
+ msgToEncrypt := Communication{}
+ msgToEncrypt.Communication = response
+ msgToEncrypt.Hash = ""
+ j, err := json.Marshal(msgToEncrypt)
+ if err != nil {
+ msgToEncrypt.Communication = ""
+ return msgToEncrypt
+ }
+ buf := strings.NewReader(string(j))
+
+ resp, err := http.Post("http://localhost:1113/encrypt", "application/json", buf)
+ if err != nil {
+ encryptedMessage.Communication = "Error connecting to internal auth API."
+ return encryptedMessage
+ }
+ defer resp.Body.Close()
+
+ // Decode the JSON response into a temporary struct
+ var responseData struct {
+ Communication string `json:"communication"`
+ Hash string `json:"hash"`
+ }
+ if err := json.NewDecoder(resp.Body).Decode(&responseData); err != nil {
+ encryptedMessage.Communication = "Error decoding JSON response from internal auth API."
+ return encryptedMessage
+ }
+
+ // Extract the desired fields and assign them to Communication struct
+ encryptedMessage.Communication = responseData.Communication
+ encryptedMessage.Hash = responseData.Hash
+
+ return encryptedMessage
+}
+
+// Interfaces with the internal API to decrypt a message and return the prompt
+// as a string.
+// TODO: Allow for additional fields for the client, ie. streaming, model, etc.
+func decryptMessage(comm Communication) Communication {
+ j, err := json.Marshal(comm)
+ if err != nil {
+ return Communication{}
+ }
+ buf := strings.NewReader(string(j))
+
+ resp, err := http.Post("http://localhost:1113/decrypt", "application/json", buf)
+ if err != nil {
+ return Communication{}
+ }
+ defer resp.Body.Close()
+
+ // Decode the JSON response into a temporary struct
+ var responseData struct {
+ Filetype string `json:"filetype"`
+ Communication string `json:"communication"`
+ Hash string `json:"hash"`
+ }
+ if err := json.NewDecoder(resp.Body).Decode(&responseData); err != nil {
+ return Communication{}
+ }
+
+ return responseData
+}
+
+func sendError(w http.ResponseWriter, msg string) {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Header().Set("Content-Type", "application/json")
+ fmt.Fprintf(w, msg)
+}
+
+func sendResponse(w http.ResponseWriter, encryptedMessage Communication) {
+ encryptedResponse, err := json.Marshal(encryptedMessage)
+ if err != nil {
+ sendError(w, "Error marshalling JSON of encrypted message.")
+ return
+ }
+
+ w.WriteHeader(http.StatusOK)
+ w.Header().Set("Content-Type", "application/json")
+ fmt.Fprintf(w, string(encryptedResponse))
+}
+
+// User HTTP GET request has the prompt decrypted and verified with a SHA-1 checksum.
+// If there's been no data corruption, re-construct user prompt for ollama
+// TODO: Eventually, add additional logic that will allow for re-direction and contextual awareness (pre-processing)
+func chat(w http.ResponseWriter, r *http.Request) {
+ model := "qwen:0.5b" // qwen:0.5b used for testing while hosting from my laptop. llama3 seems to be the best to use normally.
+
+ input, err := io.ReadAll(r.Body)
+ if err != nil {
+ return
+ }
+
+ defer r.Body.Close()
+ prompt := Communication{}
+ json.Unmarshal(input, &prompt)
+ prompt = decryptMessage(prompt)
+
+ // TODO: Add support for dynamically changing models.
+ switch prompt.Filetype {
+ case "txt":
+ model = "qwen:0.5b"
+ case "img":
+ model = "llava"
+ case "doc":
+ model = "omniparser"
+ }
+
+ newChat := Chat{}
+ newChat.Role = true
+ newChat.Content = prompt.Communication
+
+ if len(chats) == 0 {
+ chats = []Chat{newChat}
+ } else {
+ chats = append(chats, newChat)
+ }
+
+ apiCall := PromptWHistory{}
+ apiCall.Model = model
+ apiCall.Messages = chats
+
+ // TODO: Allow for this as a user setting...
+ apiCall.Stream = false
+
+ j, err := json.Marshal(apiCall)
+ buf := strings.NewReader(string(j))
+
+ // Port 11434 corresponds to Ollama. Direct access to Ollama is restricted.
+ resp, err := http.Post("http://localhost:11434/api/chat", "application/json", buf)
+ if err != nil {
+ sendError(w, "Error connecting to local process.")
+ return
+ }
+
+ output, err := io.ReadAll(resp.Body)
+ if err != nil {
+ sendError(w, "Error reading local process communication.")
+ return
+ }
+ defer r.Body.Close()
+
+ response := ResponseWHistory{}
+ json.Unmarshal(output, &response)
+ sendResponse(w, encryptMessage(response.Message.Content))
+}
+
+func generate(w http.ResponseWriter, r *http.Request) {
+ model := "qwen:0.5b" // qwen:0.5b used for testing while hosting from my laptop. llama3 seems to be the best to use normally.
+ input, err := io.ReadAll(r.Body)
+ if err != nil {
+ sendError(w, "Error reading request.")
+ return
+ }
+
+ defer r.Body.Close()
+ prompt := Communication{}
+ err = json.Unmarshal(input, &prompt)
+ if err != nil {
+ sendError(w, "Error unmarshaling JSON.")
+ return
+ }
+
+ prompt = decryptMessage(prompt)
+
+ // TODO: Add support for dynamically changing models.
+ switch prompt.Filetype {
+ case "txt":
+ model = "qwen:0.5b"
+ case "img":
+ model = "llava"
+ case "doc":
+ model = "omniparser"
+ }
+
+ apicall := "{\"model\": \""
+ apicall = apicall + model
+ apicall = apicall + "\", \"prompt\": \""
+ apicall = apicall + prompt.Communication
+ apicall = apicall + "\", \"stream\": false }"
+
+ buf := strings.NewReader(apicall)
+
+ resp, err := http.Post("http://localhost:11434/api/generate", "application/json", buf)
+ if err != nil {
+ sendError(w, "Error connecting to local process.")
+ return
+ }
+
+ output, err := io.ReadAll(resp.Body)
+ if err != nil {
+ sendError(w, "Error reading local process communication.")
+ return
+ }
+ defer resp.Body.Close()
+
+ response := Response{}
+ err = json.Unmarshal(output, &response)
+ if err != nil {
+ sendError(w, "Error unmarshaling local process JSON.")
+ return
+ }
+ sendResponse(w, encryptMessage(response.Response))
+}
+
+func serve() {
+ // TODO: Authenticate user credentials to verify they are allowed to even access
+ var wg sync.WaitGroup
+
+ endpoint := []string{
+ "/generate",
+ "/chat",
+ }
+
+ function := []func(http.ResponseWriter, *http.Request){
+ generate,
+ chat,
+ }
+
+ r := mux.NewRouter()
+ //for _, webpage := range config.Webpages {
+ //}
+ ServePage(r, config.WebpageDir, "/", config.WebpagePort)
+ ServeApi(r, config.API, endpoint, function, config.APIPort)
+ ServeAuthentication(r, config.AuthPort)
+ addr := "0.0.0.0:" + string(config.RouterPort)
+
+ srv := &http.Server{
+ Addr: addr,
+ // Good practice to set timeouts to avoid Slowloris attacks.
+ WriteTimeout: time.Second * 15,
+ ReadTimeout: time.Second * 15,
+ IdleTimeout: time.Second * 60,
+ Handler: r, // Pass our instance of gorilla/mux in.
+ }
+
+ // Run our server in a goroutine so that it doesn't block.
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+ log.Println(err)
+ }
+ }()
+
+ log.Println("Router is running on port ", config.RouterPort)
+
+ c := make(chan os.Signal, 1)
+ // We'll accept graceful shutdowns when quit via SIGINT (Ctrl+Shift+C)
+ signal.Notify(c, os.Interrupt)
+
+ // Block until we receive our signal.
+ <-c
+
+ // Create a deadline to wait for.
+ ctx, cancel := context.WithTimeout(context.Background(), time.Duration(30))
+ defer cancel()
+ // Doesn't block if no connections, but will otherwise wait
+ // until the timeout deadline.
+ srv.Shutdown(ctx)
+ // Optionally, you could run srv.Shutdown in a goroutine and block on
+ // <-ctx.Done() if your application should wait for other services
+ // to finalize based on context cancellation.
+ log.Println("shutting down")
+ wg.Wait()
+ os.Exit(0)
+}
+
+func parseCLI() {
+ // Define CLI option, shorthand, default value, and description
+ // TODO: Dynamically obtain default config location from environment variables.
+ pflag.StringP("config", "c", "/home/violet/.config/one-ai/default.json", "Configuration file used in initializing One AI.")
+ //pflag.StringP("help", "h", "false", "Display information about using the One AI CLI.")
+
+ pflag.Parse()
+ viper.BindPFlags(pflag.CommandLine)
+
+ // Retrieve CLI argument, either the default value or the user provided value.
+ CONFIG_FILE = viper.GetString("config")
+}
+
+func parseConfig() {
+ viper.SetConfigType("json")
+ viper.SetConfigFile(CONFIG_FILE)
+ viper.ReadInConfig()
+
+ // TODO: Handle error
+ viper.Unmarshal(&config)
+}
+
+func main() {
+ parseCLI()
+ parseConfig()
+ serve()
+}
diff --git a/backend/src/security-layer/container/serveAPI.go b/backend/src/security-layer/container/serveAPI.go
new file mode 100644
index 0000000..61ca8e6
--- /dev/null
+++ b/backend/src/security-layer/container/serveAPI.go
@@ -0,0 +1,42 @@
+package main
+
+import (
+ "github.com/gorilla/mux"
+ "log"
+ "net/http"
+ "time"
+)
+
+// Serve an API using mux.Router().Host({domain}).Subrouter().
+// Provide the router, domain, an array of endpoints and functions, and
+// the port you would like the API accessible to.
+func ServeApi(
+ router *mux.Router,
+ domain string,
+ endpoint []string,
+ function []func(http.ResponseWriter, *http.Request),
+ port int,
+) {
+ // TODO: Add error handling
+ apiR := router.Host(domain).Subrouter()
+
+ for i := 0; i < len(endpoint); i++ {
+ apiR.HandleFunc(endpoint[i], function[i])
+ }
+
+ apiSrv := &http.Server{
+ Addr: "0.0.0.0:" + string(port),
+ WriteTimeout: time.Second * 15,
+ ReadTimeout: time.Second * 15,
+ IdleTimeout: time.Second * 60,
+ Handler: apiR,
+ }
+
+ go func() {
+ if err := apiSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+ log.Println(err)
+ }
+ }()
+
+ log.Println("API server is running on port " + string(port))
+}
diff --git a/backend/src/security-layer/container/servePage.go b/backend/src/security-layer/container/servePage.go
new file mode 100644
index 0000000..cc9dbfb
--- /dev/null
+++ b/backend/src/security-layer/container/servePage.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+ "github.com/gorilla/mux"
+ "log"
+ "net/http"
+ "time"
+)
+
+// Serve a directory to a port.
+func ServePage(router *mux.Router, dest string, path string, port int) {
+ router.PathPrefix(path).Handler(http.FileServer(http.Dir(dest)))
+
+ srv := &http.Server{
+ Addr: "0.0.0.0: " + string(port),
+ // Good practice to set timeouts to avoid Slowloris attacks.
+ WriteTimeout: time.Second * 15,
+ ReadTimeout: time.Second * 15,
+ IdleTimeout: time.Second * 60,
+ Handler: router, // Pass our instance of gorilla/mux in.
+ }
+
+ // Run our server in a goroutine so that it doesn't block.
+ go func() {
+ if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+ log.Println(err)
+ }
+ }()
+
+ log.Println("Web server is running on port " + string(port))
+}
diff --git a/backend/src/security-layer/container/types.go b/backend/src/security-layer/container/types.go
new file mode 100644
index 0000000..7f67cac
--- /dev/null
+++ b/backend/src/security-layer/container/types.go
@@ -0,0 +1,80 @@
+package main
+
+type Communication struct {
+ Filetype string `json:"filetype"`
+ Communication string `json:"communication"`
+ Hash string `json:"hash"`
+}
+
+type Response struct {
+ Model string `json:"model"`
+ CreatedAt string `json:"created_at"`
+ Response string `json:"response"`
+ Done bool `json:"done"`
+ DoneReason string `json:"done_reason"`
+ Context []int `json:"context"`
+ TotalDuration int `json:"total_duration"`
+ LoadDuration int `json:"load_duration"`
+ PromptEC int `json:"prompt_eval_count"`
+ PromptED int `json:"prompt_eval_duration"`
+ EvalCount int `json:"eval_count"`
+ EvalDuration int `json:"eval_duration"`
+}
+
+// Chats are updated once the content is decrypted. Since this is never leaving
+// the server once decrypted, and possibly will not be saved (depending on user
+// settings), it will remain decrypted until it's time to transform into a
+// Communication JSON object, which is the HTTP response that is re-encrypted.
+
+// TODO: If the user has enabled conversation history, then save the encrypted
+// chats to the server's drive. Additionally, only accept incoming additional
+// messages from the user, rather than having the client re-send Chats already
+// stored on the server.
+type Chat struct {
+ Role bool `json:"role"`
+ Content string `json:"content"`
+}
+
+type PromptWHistory struct {
+ Model string `json:"model"`
+ Messages []Chat `json:"messages"`
+ Stream bool `json:"stream"`
+}
+
+type ResponseWHistory struct {
+ Model string `json:"model"`
+ CreatedAt string `json:"created_at"`
+ Message Chat `json:"message"`
+ Done bool `json:"done"`
+ DoneReason string `json:"done_reason"`
+ Context []int `json:"context"`
+ TotalDuration int `json:"total_duration"`
+ LoadDuration int `json:"load_duration"`
+ PromptEC int `json:"prompt_eval_count"`
+ PromptED int `json:"prompt_eval_duration"`
+ EvalCount int `json:"eval_count"`
+ EvalDuration int `json:"eval_duration"`
+}
+
+type WebpagesStructure struct {
+ Domain string `mapstructure:"domain"`
+ WebpageDir string `mapstructure:"webpage_dir"`
+ WebpagePort int `mapstructure:"webpage_port"`
+}
+
+type ConfigStructure struct {
+ TextModel string `mapstructure:"text_model"`
+ ImageModel string `mapstructure:"image_model"`
+ VideoModel string `mapstructure:"video_model"`
+ DocModel string `mapstructure:"doc_model"`
+ ResponseStream bool `mapstructure:"response_stream"`
+
+ Domain string `mapstructure:"domain"`
+ RouterPort int `mapstructure:"router_port"`
+ API string `mapstructure:"api"`
+ APIPort int `mapstructure:"api_port"`
+ AuthPort int `mapstructure:"auth_port"`
+ //Webpages WebpagesStructure `mapstructure:"webpages"`
+ WebpageDir string `mapstructure:"webpage_dir"`
+ WebpagePort int `mapstructure:"webpage_port"`
+}
diff --git a/backend/src/security-layer/init b/backend/src/security-layer/init
new file mode 100755
index 0000000..cc09fd5
--- /dev/null
+++ b/backend/src/security-layer/init
@@ -0,0 +1,9 @@
+#!/bin/zsh
+# Force delete the old container and re-initialize the container.
+sudo docker rm -f security-layer
+
+# Build the Docker image.
+sudo docker build --tag security-layer:latest .
+
+# Create a Docker container from the image and connect host port 8000 to container port 8000.
+sudo docker run --name security-layer -d -p 8000:8000 security-layer:latest
diff --git a/mobile/.DS_Store b/mobile/.DS_Store
new file mode 100644
index 0000000..62ddbf7
Binary files /dev/null and b/mobile/.DS_Store differ
diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart
index 01f926e..7006fed 100644
--- a/mobile/app/lib/main.dart
+++ b/mobile/app/lib/main.dart
@@ -4,6 +4,7 @@ import 'package:app/components/conversations_list_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:provider/provider.dart';
+import 'package:network_tools/network_tools.dart';
import 'package:app/theme.dart';
@@ -57,9 +58,23 @@ class MyApp extends ConsumerWidget {
const Center(child: ConversationsListWidget()),
];
+ Future test() async {
+ for (final ActiveHost activeHost in await MdnsScanner.searchMdnsDevices()) {
+ final MdnsInfo? mdnsInfo = await activeHost.mdnsInfo;
+ print('''
+ Address: ${activeHost.address}
+ Port: ${mdnsInfo!.mdnsPort}
+ ServiceType: ${mdnsInfo.mdnsServiceType}
+ MdnsName: ${mdnsInfo.getOnlyTheStartOfMdnsName()}
+ ''');
+ // Do anything with the active host
+ }
+
+ }
+
// This widget is the root of your application.
@override
- Widget build(BuildContext context, WidgetRef ref) {
+ Widget build(BuildContext context, WidgetRef ref) {o
final selectedIndex = ref.watch(selectedIndexProvider);
return MaterialApp(
diff --git a/mobile/app/pubspec.lock b/mobile/app/pubspec.lock
index b09a339..f60abb8 100644
--- a/mobile/app/pubspec.lock
+++ b/mobile/app/pubspec.lock
@@ -25,6 +25,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.7"
+ args:
+ dependency: transitive
+ description:
+ name: args
+ sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.0"
async:
dependency: transitive
description:
@@ -193,6 +201,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
+ csv:
+ dependency: transitive
+ description:
+ name: csv
+ sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.0"
cupertino_icons:
dependency: "direct main"
description:
@@ -201,6 +217,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.8"
+ dart_ping:
+ dependency: transitive
+ description:
+ name: dart_ping
+ sha256: "2f5418d0a5c64e53486caaac78677b25725b1e13c33c5be834ce874ea18bd24f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.0.1"
data_table_2:
dependency: transitive
description:
@@ -696,6 +720,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.0"
+ logging:
+ dependency: transitive
+ description:
+ name: logging
+ sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.0"
mapbox_search:
dependency: transitive
description:
@@ -752,6 +784,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
+ multicast_dns:
+ dependency: transitive
+ description:
+ name: multicast_dns
+ sha256: "982c4cc4cda5f98dd477bddfd623e8e4bd1014e7dbf9e7b05052e14a5b550b99"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.2+7"
native_device_orientation:
dependency: transitive
description:
@@ -768,6 +808,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
+ network_tools:
+ dependency: "direct main"
+ description:
+ name: network_tools
+ sha256: cf5726a3c3edd82de5f6d6511cb618259a083225ccc2bdb8a55d87b37014cadf
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.2"
octo_image:
dependency: transitive
description:
@@ -920,6 +968,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.10.2"
+ process_run:
+ dependency: transitive
+ description:
+ name: process_run
+ sha256: "8d9c6198b98fbbfb511edd42e7364e24d85c163e47398919871b952dc86a423e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.14.2"
provider:
dependency: "direct main"
description:
@@ -928,6 +984,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.1.2"
+ pub_semver:
+ dependency: transitive
+ description:
+ name: pub_semver
+ sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.4"
qr:
dependency: transitive
description:
@@ -984,6 +1048,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.0"
+ sembast:
+ dependency: transitive
+ description:
+ name: sembast
+ sha256: "2a768b145bee5c72ae78c2641842ee348a6d345172af1070a497293c44f65ae1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.7.1+2"
shared_preferences:
dependency: transitive
description:
@@ -1221,6 +1293,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.2"
+ universal_io:
+ dependency: transitive
+ description:
+ name: universal_io
+ sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.2"
url_launcher:
dependency: transitive
description:
@@ -1413,6 +1493,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.4"
+ yaml:
+ dependency: transitive
+ description:
+ name: yaml
+ sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.2"
sdks:
dart: ">=3.4.3 <4.0.0"
flutter: ">=3.22.0"
diff --git a/mobile/app/pubspec.yaml b/mobile/app/pubspec.yaml
index 85f8a47..8e3b2ce 100644
--- a/mobile/app/pubspec.yaml
+++ b/mobile/app/pubspec.yaml
@@ -49,6 +49,7 @@ dependencies:
camera: ^0.11.0+1
image_picker: ^1.1.2
file_picker: ^8.0.6
+ network_tools: ^5.0.2
dev_dependencies:
flutter_test:
sdk: flutter
diff --git a/output.txt b/output.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/server/requirements.txt b/server/requirements.txt
deleted file mode 100644
index fd5ad5d..0000000
--- a/server/requirements.txt
+++ /dev/null
@@ -1,267 +0,0 @@
-aiodns==3.0.0
-aiohttp==3.9.5
-aiosignal==1.3.1
-antlr4-python3-runtime==4.9.3
-anyio==3.7.1
-appdirs==1.4.4
-argcomplete==3.3.0
-argon2-cffi==21.3.0
-argon2-cffi-bindings==21.2.0
-arrow==1.2.3
-asttokens==2.4.1
-async-lru==2.0.4
-attrs==23.2.0
-Babel==2.15.0
-backcall==0.1.0
-Beaker==1.12.1
-beautifulsoup4==4.12.3
-bleach==6.1.0
-blivet==3.10.0
-blivet-gui==2.5.0
-blosc2==2.5.1
-boto3==1.34.99
-botocore==1.34.99
-Bottleneck==1.3.7
-Brlapi==0.8.5
-Brotli==1.1.0
-cachetools==5.3.2
-certifi==2023.5.7
-cffi==1.16.0
-chardet==5.2.0
-charset-normalizer==3.3.2
-click==8.1.7
-click-plugins==1.1.1
-colorama==0.4.6
-comm==0.2.1
-contourpy==1.2.0
-cryptography==41.0.7
-cssselect==1.1.0
-cupshelpers==1.0
-cycler==0.11.0
-dasbus==1.7
-dbus-python==1.3.2
-decorator==5.1.1
-defusedxml==0.7.1
-distro==1.9.0
-entrypoints==0.4
-et-xmlfile==1.1.0
-executing==2.0.1
-fastjsonschema==2.18.0
-file-magic==0.4.0
-filelock==3.14.0
-fonttools==4.50.0
-fqdn==1.5.1
-fros==1.1
-frozenlist==1.4.1
-fs==2.4.16
-fsspec==2024.3.1
-gcsfs==2023.6.0+1.g7cc53d9
-google-api-core==2.11.1
-google-auth==2.29.0
-google-auth-oauthlib==0.8.0
-google-cloud-core==2.3.3
-google-cloud-storage==2.14.0
-google-crc32c==1.5.0
-google-resumable-media==2.7.0
-googleapis-common-protos==1.63.0
-gpg==1.23.2
-greenlet==3.0.3
-grpcio==1.48.4
-grpcio-status==1.48.4
-h11==0.14.0
-html5lib==1.1
-httpcore==1.0.2
-httpx==0.26.0
-humanize==3.13.1
-idna==3.7
-inkex==1.3.1
-ipykernel==6.29.3
-ipython==8.23.0
-ipython-genutils==0.2.0
-iso639==0.1.4
-isoduration==20.11.0
-jdcal==1.4.1
-jedi==0.19.1
-Jinja2==3.1.4
-jmespath==1.0.1
-json5==0.9.24
-jsonpointer==2.3
-jsonschema==4.19.1
-jsonschema-specifications==2023.11.2
-jupyter-events==0.9.0
-jupyter-lsp==2.2.2
-jupyter_client==7.4.9
-jupyter_core==5.1.0
-jupyter_server==2.12.5
-jupyter_server_terminals==0.4.2
-jupyterlab==4.1.0
-jupyterlab_pygments==0.3.0
-jupyterlab_server==2.25.2
-kiwisolver==1.4.5
-langtable==0.0.66
-libcomps==0.1.20
-louis==3.28.0
-lxml==5.1.0
-Mako==1.2.3
-MarkupSafe==2.1.3
-matplotlib==3.8.4
-matplotlib-inline==0.1.6
-mistune==2.0.4
-mpmath==1.3.0
-msgpack==1.0.6
-multidict==6.0.4
-munkres==1.1.2
-nbclassic==0.5.6
-nbclient==0.9.0
-nbconvert==7.16.0
-nbformat==5.9.2
-ndindex==1.7
-nest-asyncio==1.6.0
-netifaces==0.11.0
-networkx==3.3
-nftables==0.1
-notebook==7.0.7
-notebook_shim==0.2.3
-numexpr==2.8.5
-numpy==1.26.4
-nvidia-cublas-cu12==12.1.3.1
-nvidia-cuda-cupti-cu12==12.1.105
-nvidia-cuda-nvrtc-cu12==12.1.105
-nvidia-cuda-runtime-cu12==12.1.105
-nvidia-cudnn-cu12==8.9.2.26
-nvidia-cufft-cu12==11.0.2.54
-nvidia-curand-cu12==10.3.2.106
-nvidia-cusolver-cu12==11.4.5.107
-nvidia-cusparse-cu12==12.1.0.106
-nvidia-nccl-cu12==2.20.5
-nvidia-nvjitlink-cu12==12.5.40
-nvidia-nvtx-cu12==12.1.105
-oauthlib==3.2.2
-odfpy==1.4.1
-olefile==0.47
-omegaconf==2.3.0
-openpyxl==3.1.2
-packaging==23.2
-pandas==2.2.1
-pandocfilters==1.5.1
-parso==0.8.3
-Paste==3.7.1
-pexpect==4.9.0
-pickleshare==0.7.5
-pid==2.2.3
-pillow==10.3.0
-platformdirs==3.11.0
-ply==3.11
-pooch==1.5.2
-productmd==1.38
-progressbar2==3.53.2
-prometheus-client==0.19.0
-prompt-toolkit==3.0.41
-protobuf==3.19.6
-psutil==5.9.8
-psycopg2==2.9.9
-ptyprocess==0.7.0
-pure-eval==0.2.2
-pwquality==1.4.5
-py-cpuinfo==9.0.0
-pyarrow==15.0.2
-pyasn1==0.5.1
-pyasn1-modules==0.3.0
-pycairo==1.25.1
-pycares==4.3.0
-pycparser==2.20
-pycrypto==2.6.1
-pycups==2.0.4
-pycurl==7.45.2
-pydub==0.25.1
-pyenchant==3.2.2
-pygit2==1.14.0
-Pygments==2.17.2
-PyGObject==3.48.2
-pykickstart==3.52
-PyMySQL==1.1.0
-PyOpenGL==3.1.7
-PyOpenGL-accelerate==3.1.7
-pyOpenSSL==23.2.0
-pyparsing==3.1.2
-pyparted==3.13.0
-PyQt5==5.15.10
-PyQt5-sip==12.13.0
-pyqtgraph==0.13.3
-pyrsistent==0.20.0
-pyserial==3.5
-PySocks==1.7.1
-python-augeas==1.1.0
-python-dateutil==2.8.2
-python-json-logger==2.0.4
-python-meh==0.51
-python-pam==2.0.2
-python-snappy==0.6.1
-python-utils==3.7.0
-pytz==2024.1
-pyudev==0.24.1
-pyxdg==0.27
-PyYAML==6.0.1
-pyzmq==25.1.1
-QtPy==2.4.1
-referencing==0.31.1
-regex==2024.4.16
-requests==2.31.0
-requests-file==2.0.0
-requests-ftp==0.3.1
-requests-oauthlib==1.3.1
-rfc3339-validator==0.1.4
-rfc3986-validator==0.1.1
-rpds-py==0.18.0
-rpm==4.19.1.1
-rpmautospec==0.6.3
-rpmautospec-core==0.1.4
-rsa==4.9
-s3transfer==0.10.1
-SciPy==1.11.3
-scour==0.38.2
-selinux @ file:///builddir/build/BUILD/libselinux-3.6/src
-Send2Trash==1.8.2
-sepolicy @ file:///builddir/build/BUILD/selinux-3.6/python/sepolicy
-setools==4.5.1
-setuptools==69.0.3
-shtab==1.6.1
-silero==0.4.1
-simpleaudio==1.0.4
-simpleline==1.9.0
-six==1.16.0
-sniffio==1.3.0
-sos==4.7.1
-sounddevice==0.4.6
-soupsieve==2.5
-SQLAlchemy==2.0.30
-stack-data==0.6.3
-sympy==1.12
-systemd-python==235
-tables==3.9.2
-tabulate==0.9.0
-Tempita==0.5.2
-termcolor==2.3.0
-terminado==0.18.0
-thrift==0.15.0
-tinycss2==1.2.1
-tldr==3.2.0
-torch==2.3.0
-torchaudio==2.3.0
-tornado==6.3.3
-traitlets==5.14.1
-typing_extensions==4.9.0
-uri-template==1.2.0
-urllib3==1.26.18
-wavio==0.0.9
-wcwidth==0.2.13
-webcolors==1.13
-webencodings==0.5.1
-websocket-client==1.3.3
-xarray==2023.8.0
-xkbregistry==0.3
-xlrd==2.0.1
-XlsxWriter==3.1.9
-xlwt==1.3.0
-yarl==1.9.2
-zstandard==0.22.0
diff --git a/server/stt.py b/server/stt.py
deleted file mode 100644
index 323ad20..0000000
--- a/server/stt.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# import required libraries
-from glob import glob
-from scipy.io.wavfile import write
-from pydub import AudioSegment
-from pydub.playback import play
-
-import os
-import torch
-import zipfile
-import torchaudio
-import sounddevice as sd
-import wave
-import wavio as wv
-
-########### Record audio
-def record_audio(audio, duration=5, freq=192000):
- # Start recorder with the given values of duration and sample frequency
- recording = sd.rec(int(duration * freq), samplerate=freq, channels=2)
-
- print('Please say your prompt. You have ' + str(duration) + ' seconds.')
- # Record audio for the given number of seconds
- sd.wait()
-
- # This will convert the NumPy array to an audio file with the given sampling frequency
- write(audio, freq, recording)
-
-def speech_to_text(audio):
- device = torch.device('cuda') # cuda also works, but our models are fast enough for CPU
- model, decoder, utils = torch.hub.load(repo_or_dir='snakers4/silero-models', model='silero_stt', language='en', device=device)
- (read_batch, split_into_batches, read_audio, prepare_model_input) = utils # see function signature for details
-
- test_files = glob(audio)
- batches = split_into_batches(test_files, batch_size=100)
- input = prepare_model_input(read_batch(batches[0]), device=device)
-
- text = ""
- output = model(input)
- for example in output:
- text += decoder(example.cpu())
-
- return text
-
-def text_to_speech():
- language = 'en'
- model_id = 'v3_en'
- sample_rate = 8000
- speaker = 'en_1'
- device = torch.device('cuda')
-
- model, example_text = torch.hub.load(repo_or_dir='snakers4/silero-models', model='silero_tts', language=language, speaker=model_id)
- model.to(device) # cuda or cpu
-
- text = open("output.txt").read()
- audio = model.apply_tts(text, speaker=speaker, sample_rate=sample_rate)
- torchaudio.save("output.mp3", audio.unsqueeze(0), sample_rate=8000)
-
- song = AudioSegment.from_mp3("output.mp3")
- play(song)
-
-def main():
- audio = "input.wav"
- record_audio(audio)
- prompt = "in 50 words or less, " + speech_to_text(audio)
- print(prompt)
-
- command = "ollama run llama3 " + prompt + " > output.txt"
- os.system(command)
- text_to_speech()
-
-
-if __name__ == "__main__":
- main()