From 8168521d922a4641d09cd0f7124abbbab3192827 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 16:07:21 +0200 Subject: [PATCH 01/23] wip --- ai/{boilerplate/goapp => }/.env | 0 ai/boilerplate/docker-compose.yml | 24 ------------------------ ai/docker-compose.yml | 24 ++++++++++++++++++++++++ ai/{boilerplate/goapp => }/go.mod | 2 +- ai/{boilerplate/goapp => }/go.sum | 0 ai/{boilerplate/goapp => }/main.go | 0 6 files changed, 25 insertions(+), 25 deletions(-) rename ai/{boilerplate/goapp => }/.env (100%) delete mode 100644 ai/boilerplate/docker-compose.yml create mode 100644 ai/docker-compose.yml rename ai/{boilerplate/goapp => }/go.mod (89%) rename ai/{boilerplate/goapp => }/go.sum (100%) rename ai/{boilerplate/goapp => }/main.go (100%) diff --git a/ai/boilerplate/goapp/.env b/ai/.env similarity index 100% rename from ai/boilerplate/goapp/.env rename to ai/.env diff --git a/ai/boilerplate/docker-compose.yml b/ai/boilerplate/docker-compose.yml deleted file mode 100644 index 8e6e1a2..0000000 --- a/ai/boilerplate/docker-compose.yml +++ /dev/null @@ -1,24 +0,0 @@ -version: '3.8' - -services: - postgres: - image: postgres:latest - environment: - POSTGRES_DB: yourdbname - POSTGRES_USER: youruser - POSTGRES_PASSWORD: yourpassword - ports: - - "5432:5432" - volumes: - - postgres-data:/var/lib/postgresql/data - - qdrant: - image: qdrant/qdrant - ports: - - "6333:6333" - volumes: - - qdrant-data:/qdrant/storage - -volumes: - postgres-data: - qdrant-data: \ No newline at end of file diff --git a/ai/docker-compose.yml b/ai/docker-compose.yml new file mode 100644 index 0000000..5649413 --- /dev/null +++ b/ai/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3.8' + +services: + postgres: + image: postgres:16.3-alpine3.20 # https://hub.docker.com/_/postgres + environment: + POSTGRES_DB: hearthands + POSTGRES_USER: hearthands + POSTGRES_PASSWORD: hearthands + ports: + - "127.0.0.1:5432:5432" # if needed, change the first 5432 to the desired local port + volumes: + - postgres-data:/var/lib/postgresql/data + + qdrant: + image: qdrant/qdrant:v1.10.0 # https://hub.docker.com/r/qdrant/qdrant + ports: + - "127.0.0.1:6333:6333" # if needed, change the first 6333 to the desired local port + volumes: + - qdrant-data:/qdrant/storage + +volumes: + postgres-data: + qdrant-data: diff --git a/ai/boilerplate/goapp/go.mod b/ai/go.mod similarity index 89% rename from ai/boilerplate/goapp/go.mod rename to ai/go.mod index 523dede..f3b3326 100644 --- a/ai/boilerplate/goapp/go.mod +++ b/ai/go.mod @@ -1,6 +1,6 @@ module goapp -go 1.22.3 +go 1.22.5 require ( github.com/joho/godotenv v1.5.1 diff --git a/ai/boilerplate/goapp/go.sum b/ai/go.sum similarity index 100% rename from ai/boilerplate/goapp/go.sum rename to ai/go.sum diff --git a/ai/boilerplate/goapp/main.go b/ai/main.go similarity index 100% rename from ai/boilerplate/goapp/main.go rename to ai/main.go From 6266d14d266b662c2e2ae4013b4d96c4b5c9f247 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 16:26:25 +0200 Subject: [PATCH 02/23] wip --- ai/docker-compose.yml | 2 +- ai/readme.md | 32 ++++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/ai/docker-compose.yml b/ai/docker-compose.yml index 5649413..868d8c4 100644 --- a/ai/docker-compose.yml +++ b/ai/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3.8' +version: '3.9' services: postgres: diff --git a/ai/readme.md b/ai/readme.md index aa5c913..7ee8bab 100644 --- a/ai/readme.md +++ b/ai/readme.md @@ -4,21 +4,39 @@ This challenge is part of the AI hiring process at Heart Hands. ## Why this challenge? -Heart Hands is operating with a small team of dedicated & talented people. We are looking for seasoned engineers with deep technical knowledge, a strong understanding of their technical stack, and excellent product intuitions to join our team. +Heart Hands is operating with a small team of dedicated & talented people. We +are looking for seasoned engineers with deep technical knowledge, a strong +understanding of their technical stack, and excellent product intuitions to join +our team. -This exercise has been designed to give a glimpse of what it is like to build an AI chief of staff app and the kind of technical challenges we face and care about. We are expecting you to spend between 4 and 6 hours on this challenge. +This exercise has been designed to give a glimpse of what it is like to build an +AI assitant and the kind of technical challenges we face and care about. We are +expecting you to spend between 4 and 6 hours on this challenge. ## Instructions -You are tasked with implementing a retrieval system for a messaging app. It must expose API endpoints that enable users to query their conversation history using natural language. You are free to implement the API in any way you feel the most comfortable with. You can find a dataset of chat conversations between several users [here](https://github.com/hearthandsinc/challenges/blob/main/ai/chats.json). +You are tasked with implementing a retrieval system for a messaging app. It must +expose API endpoints that enable users to query their conversation history using +natural language. You are free to implement the API in any way you feel the most +comfortable with. You can find a dataset of chat conversations between several +users +[here](https://github.com/hearthandsinc/challenges/blob/main/ai/chats.json). -We enforce no technical constraints: you are free to choose the language, data layer, network protocol, and design your API as you see fit. You are purposefully being given a lot of freedom here, and you will not be judged on these decisions alone, but we will challenge the understanding of the trade-offs you make. +We enforce no technical constraints: you are free to choose the language, data +layer, network protocol, and design your API as you see fit. You are +purposefully being given a lot of freedom here, and you will not be judged on +these decisions alone, but we will challenge the understanding of the trade-offs +you make. -You can find a simple boilerplate for a Go/PGSQL/Qdrant app [here](https://github.com/hearthandsinc/challenges/tree/main/ai/boilterplate), but again, you are free to use any technology you want. +You can find a simple boilerplate for a Go/PGSQL/Qdrant app +[here](https://github.com/hearthandsinc/challenges/tree/main/ai/boilterplate), +but again, you are free to use any technology you want. Functional requirements: -- [ ] Users can search through their chat history using natural language. To evaluate the solution, we'll be asking questions from Matthias, Aymeric and David's point of view: +- [ ] Users can search through their chat history using natural language. To + evaluate the solution, we'll be asking questions from Matthias, Aymeric and + David's point of view: - Matthias' id is: `018f2685-a936-44a9-8ff4-9ef0c98289b8` - Aymeric's id is: `82247543-5c2d-46d9-9a0d-fe25482922b5` - David's id is: `2d614bef-2b01-4021-b63a-9d04658536f3` @@ -29,8 +47,6 @@ Functional requirements: - [ ] Your solution is multitenant. Users can only search their own history using their user ID. - [ ] Users can query their chat history through an API endpoint. - - ## Bonus Some topics that we find interesting to dig into: From f186ea8ee7c5b49f77f9ae2bca2088ccaaea31fc Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 16:33:13 +0200 Subject: [PATCH 03/23] wip --- .github/workflows/ci.yml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 535c45f..fe4ff81 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,12 +11,15 @@ on: - "**.md" env: - GO_VERSION: "1.21.4" # https://go.dev/dl/ - GOLANGCI_LINT_VERSION: "v1.55.2" # https://github.com/golangci/golangci-lint/releases + GO_VERSION: "1.22.5" # https://go.dev/dl/ + GOLANGCI_LINT_VERSION: "v1.59.1" # https://github.com/golangci/golangci-lint/releases jobs: lint: runs-on: ubuntu-latest + strategy: + matrix: + cwd: ["./ios/server", "./ai"] steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 @@ -27,23 +30,29 @@ jobs: with: version: ${{ env.GOLANGCI_LINT_VERSION }} skip-cache: true - working-directory: ./ios/server + working-directory: ${{ matrix.cwd }} security: runs-on: ubuntu-latest + strategy: + matrix: + cwd: ["./ios/server", "./ai"] steps: - uses: golang/govulncheck-action@v1 with: go-version-input: ${{ env.GO_VERSION }} - work-dir: ./ios/server + work-dir: ${{ matrix.cwd }} build: runs-on: ubuntu-latest + strategy: + matrix: + cwd: ["./ios/server", "./ai"] steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: go-version: ${{ env.GO_VERSION }} - name: Build go code - working-directory: ./ios/server + working-directory: ${{ matrix.cwd }} run: go build From 7c2a6342d0a26a6c1363144a456bcd2b98fcb2c9 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 16:46:21 +0200 Subject: [PATCH 04/23] wip --- ai/.env | 9 +++++---- ai/main.go | 36 +++++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/ai/.env b/ai/.env index fe3953b..88a36b8 100644 --- a/ai/.env +++ b/ai/.env @@ -1,7 +1,8 @@ POSTGRES_HOST=localhost POSTGRES_PORT=5432 -POSTGRES_USER=youruser -POSTGRES_PASSWORD=yourpassword -POSTGRES_DB=yourdbname +POSTGRES_USER=hearthands +POSTGRES_PASSWORD=hearthands +POSTGRES_DB=hearthands + QDRANT_HOST=localhost -QDRANT_PORT=6333 \ No newline at end of file +QDRANT_PORT=6333 diff --git a/ai/main.go b/ai/main.go index 5b28abc..831ac12 100644 --- a/ai/main.go +++ b/ai/main.go @@ -3,8 +3,11 @@ package main import ( "database/sql" "fmt" + "io" "log" + "net" "net/http" + "net/url" "os" "github.com/joho/godotenv" @@ -18,26 +21,41 @@ func main() { log.Fatalf("Error loading .env file: %v", err) } - // Connect to PostgreSQL - pgConnStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", - os.Getenv("POSTGRES_HOST"), os.Getenv("POSTGRES_PORT"), os.Getenv("POSTGRES_USER"), os.Getenv("POSTGRES_PASSWORD"), os.Getenv("POSTGRES_DB")) - db, err := sql.Open("postgres", pgConnStr) + // Connect to the PostgreSQL instance + db, err := sql.Open("postgres", (&url.URL{ + Scheme: "postgres", + User: url.UserPassword(os.Getenv("POSTGRES_USER"), os.Getenv("POSTGRES_PASSWORD")), + Host: net.JoinHostPort(os.Getenv("POSTGRES_HOST"), os.Getenv("POSTGRES_PORT")), + Path: os.Getenv("POSTGRES_DB"), + }).String()) if err != nil { log.Fatalf("Error connecting to PostgreSQL: %v", err) } defer db.Close() - // Connect to Qdrant - qdrantURL := fmt.Sprintf("http://%s:%s", os.Getenv("QDRANT_HOST"), os.Getenv("QDRANT_PORT")) - resp, err := http.Get(qdrantURL + "/v1/collections") + // Query the Qdrant instance + qdrantBaseURL := (&url.URL{ + Scheme: "http", + Host: net.JoinHostPort(os.Getenv("QDRANT_HOST"), os.Getenv("QDRANT_PORT")), + }).String() + resp, err := http.Get(qdrantBaseURL + "/v1/collections") if err != nil { log.Fatalf("Error connecting to Qdrant: %v", err) } defer resp.Body.Close() - // Simple HTTP server + // Declare your http routes + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Connected to PostgreSQL and Qdrant successfully!") + w.WriteHeader(http.StatusOK) + io.WriteString(w, "Hello, World!") + }) + + http.HandleFunc("/livez", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) }) + + // Start the server, which will listen on http://localhost:8080 + fmt.Println("Starting server on port 8080") log.Fatal(http.ListenAndServe(":8080", nil)) } From 7bca92535d2761ab29a1437a9293397274e8ef35 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 16:48:17 +0200 Subject: [PATCH 05/23] wip --- ai/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ai/main.go b/ai/main.go index 831ac12..a13f076 100644 --- a/ai/main.go +++ b/ai/main.go @@ -48,7 +48,7 @@ func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - io.WriteString(w, "Hello, World!") + _, _ = io.WriteString(w, "Hello, World!") }) http.HandleFunc("/livez", func(w http.ResponseWriter, r *http.Request) { From a7b8d31bd3ee6fe9c1d44bb27308d946a24b3eb7 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 16:48:55 +0200 Subject: [PATCH 06/23] wip --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 0b2b5f5..fe0df31 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,7 @@ # Challenges [![ci status](https://github.com/hearthandsinc/challenges/actions/workflows/ci.yml/badge.svg)](https://github.com/hearthandsinc/challenges/actions/workflows/ci.yml) This repository contains the [Heart Hands](https://hearthands.tech/) engineering -challenges for [iOS](./ios), [backend](./backend), and [AI](./aillm) positions. +challenges for [iOS](./ios), [backend](./backend), and [AI](./ai) positions. ## Feedbacks From d7ed433bb33a461e06e239e93ad3acaf3218d844 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 16:49:41 +0200 Subject: [PATCH 07/23] wip --- ios/server/go.mod | 6 +++--- ios/server/go.sum | 15 ++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/ios/server/go.mod b/ios/server/go.mod index 2ee7061..021070a 100644 --- a/ios/server/go.mod +++ b/ios/server/go.mod @@ -3,14 +3,14 @@ module github.com/hearthands/challenges/ios/server go 1.21.4 require ( - github.com/go-chi/chi/v5 v5.0.10 + github.com/go-chi/chi/v5 v5.1.0 github.com/go-chi/render v1.0.3 github.com/r3labs/sse/v2 v2.10.0 - golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa + golang.org/x/exp v0.0.0-20240707233637-46b078467d37 ) require ( github.com/ajg/form v1.5.1 // indirect - golang.org/x/net v0.18.0 // indirect + golang.org/x/net v0.27.0 // indirect gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect ) diff --git a/ios/server/go.sum b/ios/server/go.sum index f20900c..3df15b6 100644 --- a/ios/server/go.sum +++ b/ios/server/go.sum @@ -2,8 +2,8 @@ github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= -github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -14,14 +14,11 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= -golang.org/x/net v0.0.0-20191116160921-f9c825593386 h1:ktbWvQrW08Txdxno1PiDpSxPXG6ndGsfnJjRRtkM0LQ= +golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w= +golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y= From 01872f75dad473fd917887b449962062f4c6d8a3 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 16:49:49 +0200 Subject: [PATCH 08/23] wip --- ios/server/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/server/go.mod b/ios/server/go.mod index 021070a..31fe95e 100644 --- a/ios/server/go.mod +++ b/ios/server/go.mod @@ -1,6 +1,6 @@ module github.com/hearthands/challenges/ios/server -go 1.21.4 +go 1.22.5 require ( github.com/go-chi/chi/v5 v5.1.0 From 095cacb04a8bd230185bc5b18205692a371d2037 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 16:58:20 +0200 Subject: [PATCH 09/23] wip --- ai/go.mod | 1 + ai/go.sum | 2 ++ ai/main.go | 40 ++++++++++++++++++++++++++++++---------- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/ai/go.mod b/ai/go.mod index f3b3326..99b1d37 100644 --- a/ai/go.mod +++ b/ai/go.mod @@ -3,6 +3,7 @@ module goapp go 1.22.5 require ( + github.com/go-chi/chi/v5 v5.1.0 github.com/joho/godotenv v1.5.1 github.com/lib/pq v1.10.9 ) diff --git a/ai/go.sum b/ai/go.sum index ecb9035..49887b7 100644 --- a/ai/go.sum +++ b/ai/go.sum @@ -1,3 +1,5 @@ +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= diff --git a/ai/main.go b/ai/main.go index a13f076..308db0c 100644 --- a/ai/main.go +++ b/ai/main.go @@ -4,21 +4,27 @@ import ( "database/sql" "fmt" "io" - "log" "net" "net/http" "net/url" "os" + "strings" + "github.com/go-chi/chi/v5" "github.com/joho/godotenv" _ "github.com/lib/pq" ) +var ( + hostname = envOrDefault("HTTP_HOST", "localhost") // hostname used by the server + port = envOrDefault("PORT", "3000") // port used by the server +) + func main() { // Load .env file err := godotenv.Load() if err != nil { - log.Fatalf("Error loading .env file: %v", err) + panic(fmt.Errorf("couldn't loading .env file: %w", err)) } // Connect to the PostgreSQL instance @@ -29,7 +35,7 @@ func main() { Path: os.Getenv("POSTGRES_DB"), }).String()) if err != nil { - log.Fatalf("Error connecting to PostgreSQL: %v", err) + panic(fmt.Errorf("failed ot connect to PostgreSQL: %w", err)) } defer db.Close() @@ -40,22 +46,36 @@ func main() { }).String() resp, err := http.Get(qdrantBaseURL + "/v1/collections") if err != nil { - log.Fatalf("Error connecting to Qdrant: %v", err) + panic(fmt.Errorf("failed to query Qdrant DB: %w", err)) } defer resp.Body.Close() // Declare your http routes - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + r := chi.NewRouter() + + r.Get("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) _, _ = io.WriteString(w, "Hello, World!") - }) + })) - http.HandleFunc("/livez", func(w http.ResponseWriter, r *http.Request) { + http.HandleFunc("/livez", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - }) + })) // Start the server, which will listen on http://localhost:8080 - fmt.Println("Starting server on port 8080") - log.Fatal(http.ListenAndServe(":8080", nil)) + addr := net.JoinHostPort("localhost", "8080") + fmt.Printf("Starting server on %s\n", addr) + if err := http.ListenAndServe(addr, r); err != nil { + panic(fmt.Errorf("failed to start server on %s: %w", addr, err)) + } +} + +// envOrDefault returns the value of the environment variable at the given key. +// Fallbacks to the given default if the value found is missing or empty. +func envOrDefault(key string, defaultValue string) string { + if v := strings.TrimSpace(os.Getenv(key)); len(v) > 0 { + return v + } + return defaultValue } From 730ae7094dfe34bed14a99f624da1ec46cc9762f Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 16:58:47 +0200 Subject: [PATCH 10/23] wip --- ios/server/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ios/server/main.go b/ios/server/main.go index 7457c45..40825b0 100644 --- a/ios/server/main.go +++ b/ios/server/main.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "math/rand" + "net" "net/http" "os" "strconv" @@ -49,7 +50,7 @@ func main() { r.Post("/chats/{chatID}/messages", app.PostMessages) }) - addr := fmt.Sprintf("%s:%s", hostname, port) + addr := net.JoinHostPort(hostname, port) fmt.Printf("Starting server on %s\n", addr) if err := http.ListenAndServe(addr, r); err != nil { panic(fmt.Errorf("failed to start server on %s: %w", addr, err)) From 2fade33ebb37be7983f0789dccb2548b9095ba5f Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 16:59:08 +0200 Subject: [PATCH 11/23] wip --- ai/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ai/main.go b/ai/main.go index 308db0c..f785cfa 100644 --- a/ai/main.go +++ b/ai/main.go @@ -22,8 +22,7 @@ var ( func main() { // Load .env file - err := godotenv.Load() - if err != nil { + if err := godotenv.Load(); err != nil { panic(fmt.Errorf("couldn't loading .env file: %w", err)) } From 695107eef73db129c9d1d10da2dd2bef3d897027 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 18:02:48 +0200 Subject: [PATCH 12/23] wip --- ai/main.go | 7 +++++-- ios/server/main.go | 11 ++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ai/main.go b/ai/main.go index f785cfa..4529c1f 100644 --- a/ai/main.go +++ b/ai/main.go @@ -16,8 +16,11 @@ import ( ) var ( - hostname = envOrDefault("HTTP_HOST", "localhost") // hostname used by the server - port = envOrDefault("PORT", "3000") // port used by the server + // hostname used by the server + hostname = envOrDefault("HTTP_HOST", "localhost") + + // port used by the server + port = envOrDefault("PORT", "3000") ) func main() { diff --git a/ios/server/main.go b/ios/server/main.go index 40825b0..67bd555 100644 --- a/ios/server/main.go +++ b/ios/server/main.go @@ -22,9 +22,14 @@ import ( ) var ( - hostname = envOrDefault("HTTP_HOST", "localhost") // hostname used by the server - port = envOrDefault("PORT", "3000") // port used by the server - limit = 100 // maximum number of entities returned in a single request + // hostname used by the server + hostname = envOrDefault("HTTP_HOST", "localhost") + + // port used by the server + port = envOrDefault("PORT", "3000") + + // maximum number of entities returned in a single request + limit = 100 ) //go:embed data.json From e34cde072d71de8ee97b6beaaa3ac11acb8c7d78 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 18:05:44 +0200 Subject: [PATCH 13/23] wip --- ai/main.go | 13 ++++++++----- ios/server/main.go | 6 +++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/ai/main.go b/ai/main.go index 4529c1f..d18f318 100644 --- a/ai/main.go +++ b/ai/main.go @@ -20,7 +20,7 @@ var ( hostname = envOrDefault("HTTP_HOST", "localhost") // port used by the server - port = envOrDefault("PORT", "3000") + port = envOrDefault("PORT", "8080") ) func main() { @@ -57,22 +57,25 @@ func main() { r := chi.NewRouter() r.Get("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) _, _ = io.WriteString(w, "Hello, World!") })) - http.HandleFunc("/livez", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + r.Get("/livez", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) // Start the server, which will listen on http://localhost:8080 - addr := net.JoinHostPort("localhost", "8080") - fmt.Printf("Starting server on %s\n", addr) + addr := net.JoinHostPort(hostname, port) + fmt.Printf("Starting server on http://%s\n", addr) if err := http.ListenAndServe(addr, r); err != nil { panic(fmt.Errorf("failed to start server on %s: %w", addr, err)) } } +// +// Helpers +// + // envOrDefault returns the value of the environment variable at the given key. // Fallbacks to the given default if the value found is missing or empty. func envOrDefault(key string, defaultValue string) string { diff --git a/ios/server/main.go b/ios/server/main.go index 67bd555..44feffb 100644 --- a/ios/server/main.go +++ b/ios/server/main.go @@ -55,8 +55,12 @@ func main() { r.Post("/chats/{chatID}/messages", app.PostMessages) }) + r.Get("/livez", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + addr := net.JoinHostPort(hostname, port) - fmt.Printf("Starting server on %s\n", addr) + fmt.Printf("Starting server on http://%s\n", addr) if err := http.ListenAndServe(addr, r); err != nil { panic(fmt.Errorf("failed to start server on %s: %w", addr, err)) } From a1aabc63969343f7f8c5fb8bac4c4676733b6fc1 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 18:06:33 +0200 Subject: [PATCH 14/23] wip --- ai/main.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ai/main.go b/ai/main.go index d18f318..cfbbf34 100644 --- a/ai/main.go +++ b/ai/main.go @@ -9,8 +9,10 @@ import ( "net/url" "os" "strings" + "time" "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" "github.com/joho/godotenv" _ "github.com/lib/pq" ) @@ -56,6 +58,10 @@ func main() { r := chi.NewRouter() + r.Use(middleware.Logger) + r.Use(middleware.Recoverer) + r.Use(middleware.Timeout(60 * time.Second)) + r.Get("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = io.WriteString(w, "Hello, World!") })) From 38bd12238111fd18e26d240360ae6a4883eb60c5 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 18:07:17 +0200 Subject: [PATCH 15/23] wip --- ai/main.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ai/main.go b/ai/main.go index cfbbf34..1064dff 100644 --- a/ai/main.go +++ b/ai/main.go @@ -54,7 +54,7 @@ func main() { } defer resp.Body.Close() - // Declare your http routes + // Create the HTTP server r := chi.NewRouter() @@ -62,6 +62,8 @@ func main() { r.Use(middleware.Recoverer) r.Use(middleware.Timeout(60 * time.Second)) + // TODO: add your own routes here + r.Get("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = io.WriteString(w, "Hello, World!") })) @@ -70,7 +72,7 @@ func main() { w.WriteHeader(http.StatusOK) })) - // Start the server, which will listen on http://localhost:8080 + // Start the HTTP server addr := net.JoinHostPort(hostname, port) fmt.Printf("Starting server on http://%s\n", addr) if err := http.ListenAndServe(addr, r); err != nil { From 88c632046da92b13520dd7116c3c938db52c46ea Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 18:12:08 +0200 Subject: [PATCH 16/23] wip --- ai/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ai/readme.md b/ai/readme.md index 7ee8bab..92321c5 100644 --- a/ai/readme.md +++ b/ai/readme.md @@ -10,7 +10,7 @@ understanding of their technical stack, and excellent product intuitions to join our team. This exercise has been designed to give a glimpse of what it is like to build an -AI assitant and the kind of technical challenges we face and care about. We are +AI assistant and the kind of technical challenges we face and care about. We are expecting you to spend between 4 and 6 hours on this challenge. ## Instructions From 11ba2f5dcb56ab845c1adff09d6050fc591c07aa Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 18:12:21 +0200 Subject: [PATCH 17/23] wip --- ai/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ai/readme.md b/ai/readme.md index 92321c5..e687c02 100644 --- a/ai/readme.md +++ b/ai/readme.md @@ -37,9 +37,9 @@ Functional requirements: - [ ] Users can search through their chat history using natural language. To evaluate the solution, we'll be asking questions from Matthias, Aymeric and David's point of view: -- Matthias' id is: `018f2685-a936-44a9-8ff4-9ef0c98289b8` -- Aymeric's id is: `82247543-5c2d-46d9-9a0d-fe25482922b5` -- David's id is: `2d614bef-2b01-4021-b63a-9d04658536f3` + - Matthias' id is: `018f2685-a936-44a9-8ff4-9ef0c98289b8` + - Aymeric's id is: `82247543-5c2d-46d9-9a0d-fe25482922b5` + - David's id is: `2d614bef-2b01-4021-b63a-9d04658536f3` - Example of the questions we will be asking: - "What did Aymeric and I discuss last week?" - "Where does Aymeric live?" From 432b73ef6bf8e4ccccf5e2d7391684c1cfb80672 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 18:13:34 +0200 Subject: [PATCH 18/23] wip --- ai/readme.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ai/readme.md b/ai/readme.md index e687c02..13d85d7 100644 --- a/ai/readme.md +++ b/ai/readme.md @@ -37,13 +37,11 @@ Functional requirements: - [ ] Users can search through their chat history using natural language. To evaluate the solution, we'll be asking questions from Matthias, Aymeric and David's point of view: - - Matthias' id is: `018f2685-a936-44a9-8ff4-9ef0c98289b8` - - Aymeric's id is: `82247543-5c2d-46d9-9a0d-fe25482922b5` - - David's id is: `2d614bef-2b01-4021-b63a-9d04658536f3` -- Example of the questions we will be asking: - - "What did Aymeric and I discuss last week?" - - "Where does Aymeric live?" - - "What did David tell me about the roadmap?" + - Matthias' id is: `018f2685-a936-44a9-8ff4-9ef0c98289b8`, Aymeric's id is: `82247543-5c2d-46d9-9a0d-fe25482922b5`, David's id is: `2d614bef-2b01-4021-b63a-9d04658536f3` + - Example of the questions we will be asking: + - "What did Aymeric and I discuss last week?" + - "Where does Aymeric live?" + - "What did David tell me about the roadmap?" - [ ] Your solution is multitenant. Users can only search their own history using their user ID. - [ ] Users can query their chat history through an API endpoint. From 12e36d6c3ae5777df9e3a69e98f2fca9ac09f6b4 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 18:15:19 +0200 Subject: [PATCH 19/23] wip --- ai/readme.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ai/readme.md b/ai/readme.md index 13d85d7..236b5d1 100644 --- a/ai/readme.md +++ b/ai/readme.md @@ -28,9 +28,10 @@ purposefully being given a lot of freedom here, and you will not be judged on these decisions alone, but we will challenge the understanding of the trade-offs you make. -You can find a simple boilerplate for a Go/PGSQL/Qdrant app -[here](https://github.com/hearthandsinc/challenges/tree/main/ai/boilterplate), -but again, you are free to use any technology you want. +This challenge contains a Go/PostgreSQL/Qdrant boilerplate that you can use as a +starting point: +- start the stack with `docker-compose up` +- start the backend with `go run .` Functional requirements: From d3899767f295e2138573c332c3889be997109d48 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 18:16:25 +0200 Subject: [PATCH 20/23] wip --- ai/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ai/readme.md b/ai/readme.md index 236b5d1..c363afa 100644 --- a/ai/readme.md +++ b/ai/readme.md @@ -28,10 +28,10 @@ purposefully being given a lot of freedom here, and you will not be judged on these decisions alone, but we will challenge the understanding of the trade-offs you make. -This challenge contains a Go/PostgreSQL/Qdrant boilerplate that you can use as a +To save time, we also provide a Go/PostgreSQL/Qdrant boilerplate that you can use as a starting point: -- start the stack with `docker-compose up` -- start the backend with `go run .` +- start the stack with `cd ./ai && docker-compose up` +- start the backend with `cd ./ai && go run .` Functional requirements: From acfa91804cb032de3e5aeac9f6cfbec3075501bb Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Wed, 10 Jul 2024 18:20:49 +0200 Subject: [PATCH 21/23] wip --- ai/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ai/main.go b/ai/main.go index 1064dff..fc488da 100644 --- a/ai/main.go +++ b/ai/main.go @@ -62,8 +62,6 @@ func main() { r.Use(middleware.Recoverer) r.Use(middleware.Timeout(60 * time.Second)) - // TODO: add your own routes here - r.Get("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = io.WriteString(w, "Hello, World!") })) @@ -72,6 +70,8 @@ func main() { w.WriteHeader(http.StatusOK) })) + // TODO: add your own routes here + // Start the HTTP server addr := net.JoinHostPort(hostname, port) fmt.Printf("Starting server on http://%s\n", addr) From 47ed8b0ed9a403658a572294982fa6bc7ccad666 Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Fri, 12 Jul 2024 19:32:37 +0200 Subject: [PATCH 22/23] wip --- ai/.env | 8 ---- ai/chats.json | 3 -- ai/docker-compose.yml | 24 ----------- ai/go.mod | 9 ----- ai/go.sum | 6 --- ai/main.go | 94 ------------------------------------------- ai/readme.md | 74 ---------------------------------- readme.md | 2 +- 8 files changed, 1 insertion(+), 219 deletions(-) delete mode 100644 ai/.env delete mode 100644 ai/chats.json delete mode 100644 ai/docker-compose.yml delete mode 100644 ai/go.mod delete mode 100644 ai/go.sum delete mode 100644 ai/main.go delete mode 100644 ai/readme.md diff --git a/ai/.env b/ai/.env deleted file mode 100644 index 88a36b8..0000000 --- a/ai/.env +++ /dev/null @@ -1,8 +0,0 @@ -POSTGRES_HOST=localhost -POSTGRES_PORT=5432 -POSTGRES_USER=hearthands -POSTGRES_PASSWORD=hearthands -POSTGRES_DB=hearthands - -QDRANT_HOST=localhost -QDRANT_PORT=6333 diff --git a/ai/chats.json b/ai/chats.json deleted file mode 100644 index 2e6016f..0000000 --- a/ai/chats.json +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:20f82c13ec0d446b79a3035932aeb2da6d55bb484e8642204f1327864da6a879 -size 59219437 diff --git a/ai/docker-compose.yml b/ai/docker-compose.yml deleted file mode 100644 index 868d8c4..0000000 --- a/ai/docker-compose.yml +++ /dev/null @@ -1,24 +0,0 @@ -version: '3.9' - -services: - postgres: - image: postgres:16.3-alpine3.20 # https://hub.docker.com/_/postgres - environment: - POSTGRES_DB: hearthands - POSTGRES_USER: hearthands - POSTGRES_PASSWORD: hearthands - ports: - - "127.0.0.1:5432:5432" # if needed, change the first 5432 to the desired local port - volumes: - - postgres-data:/var/lib/postgresql/data - - qdrant: - image: qdrant/qdrant:v1.10.0 # https://hub.docker.com/r/qdrant/qdrant - ports: - - "127.0.0.1:6333:6333" # if needed, change the first 6333 to the desired local port - volumes: - - qdrant-data:/qdrant/storage - -volumes: - postgres-data: - qdrant-data: diff --git a/ai/go.mod b/ai/go.mod deleted file mode 100644 index 99b1d37..0000000 --- a/ai/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module goapp - -go 1.22.5 - -require ( - github.com/go-chi/chi/v5 v5.1.0 - github.com/joho/godotenv v1.5.1 - github.com/lib/pq v1.10.9 -) diff --git a/ai/go.sum b/ai/go.sum deleted file mode 100644 index 49887b7..0000000 --- a/ai/go.sum +++ /dev/null @@ -1,6 +0,0 @@ -github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= -github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= diff --git a/ai/main.go b/ai/main.go deleted file mode 100644 index fc488da..0000000 --- a/ai/main.go +++ /dev/null @@ -1,94 +0,0 @@ -package main - -import ( - "database/sql" - "fmt" - "io" - "net" - "net/http" - "net/url" - "os" - "strings" - "time" - - "github.com/go-chi/chi/v5" - "github.com/go-chi/chi/v5/middleware" - "github.com/joho/godotenv" - _ "github.com/lib/pq" -) - -var ( - // hostname used by the server - hostname = envOrDefault("HTTP_HOST", "localhost") - - // port used by the server - port = envOrDefault("PORT", "8080") -) - -func main() { - // Load .env file - if err := godotenv.Load(); err != nil { - panic(fmt.Errorf("couldn't loading .env file: %w", err)) - } - - // Connect to the PostgreSQL instance - db, err := sql.Open("postgres", (&url.URL{ - Scheme: "postgres", - User: url.UserPassword(os.Getenv("POSTGRES_USER"), os.Getenv("POSTGRES_PASSWORD")), - Host: net.JoinHostPort(os.Getenv("POSTGRES_HOST"), os.Getenv("POSTGRES_PORT")), - Path: os.Getenv("POSTGRES_DB"), - }).String()) - if err != nil { - panic(fmt.Errorf("failed ot connect to PostgreSQL: %w", err)) - } - defer db.Close() - - // Query the Qdrant instance - qdrantBaseURL := (&url.URL{ - Scheme: "http", - Host: net.JoinHostPort(os.Getenv("QDRANT_HOST"), os.Getenv("QDRANT_PORT")), - }).String() - resp, err := http.Get(qdrantBaseURL + "/v1/collections") - if err != nil { - panic(fmt.Errorf("failed to query Qdrant DB: %w", err)) - } - defer resp.Body.Close() - - // Create the HTTP server - - r := chi.NewRouter() - - r.Use(middleware.Logger) - r.Use(middleware.Recoverer) - r.Use(middleware.Timeout(60 * time.Second)) - - r.Get("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, _ = io.WriteString(w, "Hello, World!") - })) - - r.Get("/livez", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - })) - - // TODO: add your own routes here - - // Start the HTTP server - addr := net.JoinHostPort(hostname, port) - fmt.Printf("Starting server on http://%s\n", addr) - if err := http.ListenAndServe(addr, r); err != nil { - panic(fmt.Errorf("failed to start server on %s: %w", addr, err)) - } -} - -// -// Helpers -// - -// envOrDefault returns the value of the environment variable at the given key. -// Fallbacks to the given default if the value found is missing or empty. -func envOrDefault(key string, defaultValue string) string { - if v := strings.TrimSpace(os.Getenv(key)); len(v) > 0 { - return v - } - return defaultValue -} diff --git a/ai/readme.md b/ai/readme.md deleted file mode 100644 index c363afa..0000000 --- a/ai/readme.md +++ /dev/null @@ -1,74 +0,0 @@ -# AI Challenge - -This challenge is part of the AI hiring process at Heart Hands. - -## Why this challenge? - -Heart Hands is operating with a small team of dedicated & talented people. We -are looking for seasoned engineers with deep technical knowledge, a strong -understanding of their technical stack, and excellent product intuitions to join -our team. - -This exercise has been designed to give a glimpse of what it is like to build an -AI assistant and the kind of technical challenges we face and care about. We are -expecting you to spend between 4 and 6 hours on this challenge. - -## Instructions - -You are tasked with implementing a retrieval system for a messaging app. It must -expose API endpoints that enable users to query their conversation history using -natural language. You are free to implement the API in any way you feel the most -comfortable with. You can find a dataset of chat conversations between several -users -[here](https://github.com/hearthandsinc/challenges/blob/main/ai/chats.json). - -We enforce no technical constraints: you are free to choose the language, data -layer, network protocol, and design your API as you see fit. You are -purposefully being given a lot of freedom here, and you will not be judged on -these decisions alone, but we will challenge the understanding of the trade-offs -you make. - -To save time, we also provide a Go/PostgreSQL/Qdrant boilerplate that you can use as a -starting point: -- start the stack with `cd ./ai && docker-compose up` -- start the backend with `cd ./ai && go run .` - -Functional requirements: - -- [ ] Users can search through their chat history using natural language. To - evaluate the solution, we'll be asking questions from Matthias, Aymeric and - David's point of view: - - Matthias' id is: `018f2685-a936-44a9-8ff4-9ef0c98289b8`, Aymeric's id is: `82247543-5c2d-46d9-9a0d-fe25482922b5`, David's id is: `2d614bef-2b01-4021-b63a-9d04658536f3` - - Example of the questions we will be asking: - - "What did Aymeric and I discuss last week?" - - "Where does Aymeric live?" - - "What did David tell me about the roadmap?" -- [ ] Your solution is multitenant. Users can only search their own history using their user ID. -- [ ] Users can query their chat history through an API endpoint. - -## Bonus - -Some topics that we find interesting to dig into: - -- [ ] Ability to target a specific date. -E.g.: - - "Can you recap last Thursday's chat activity?" - - "Can you summarize our discussion with David from March 12th?" -- [ ] Ability to count occurrences. -E.g.: - - "How many messages did Aymeric and I exchange so far?" -- [ ] The assistant chat keeps track of one request to the next. -E.g.: - 1. "What are Aymeric's requirements for buying a new home?" - 2. "What are David's?" - -## Challenge Review - -We know you only have a limited time allotted to deliver this challenge, and thus will have to prioritize what you work on. A few things that are important for us and that will be considered during the review: - -- **documentation**: is the README clear? are important parts of the code documented? -- **impact**: which features did you prioritize? -- **maintainability**: is the code well-structured and easy to read/evolve? -- **robustness**: are edge cases considered? - -Good luck, and enjoy! diff --git a/readme.md b/readme.md index fe0df31..6789894 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,7 @@ # Challenges [![ci status](https://github.com/hearthandsinc/challenges/actions/workflows/ci.yml/badge.svg)](https://github.com/hearthandsinc/challenges/actions/workflows/ci.yml) This repository contains the [Heart Hands](https://hearthands.tech/) engineering -challenges for [iOS](./ios), [backend](./backend), and [AI](./ai) positions. +challenges for [iOS](./ios) and [backend](./backend) positions. ## Feedbacks From b68e1ba72b166d70a181ad07435f96b3dff6fdea Mon Sep 17 00:00:00 2001 From: Aymeric Beaumet Date: Fri, 12 Jul 2024 19:35:38 +0200 Subject: [PATCH 23/23] wip --- .github/workflows/ci.yml | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe4ff81..3ed2087 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,9 +17,6 @@ env: jobs: lint: runs-on: ubuntu-latest - strategy: - matrix: - cwd: ["./ios/server", "./ai"] steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 @@ -30,29 +27,23 @@ jobs: with: version: ${{ env.GOLANGCI_LINT_VERSION }} skip-cache: true - working-directory: ${{ matrix.cwd }} + working-directory: ./ios/server security: runs-on: ubuntu-latest - strategy: - matrix: - cwd: ["./ios/server", "./ai"] steps: - uses: golang/govulncheck-action@v1 with: go-version-input: ${{ env.GO_VERSION }} - work-dir: ${{ matrix.cwd }} + work-dir: ./ios/server build: runs-on: ubuntu-latest - strategy: - matrix: - cwd: ["./ios/server", "./ai"] steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: go-version: ${{ env.GO_VERSION }} - name: Build go code - working-directory: ${{ matrix.cwd }} + working-directory: ./ios/server run: go build