Skip to content

Commit 8416249

Browse files
committed
feat(backend): implement
Signed-off-by: Aryan Goyal <[email protected]>
1 parent 6410ed4 commit 8416249

File tree

9 files changed

+423
-5
lines changed

9 files changed

+423
-5
lines changed

.env.example

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
MODE=dev
2+
PORT=8000
3+
GEMINI_KEY=

.gitignore

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
main
2+
tmp
23
.env
3-
extension/chromium/socratic-code/node_modules
4-
extension/chromium/socratic-code/.svelte-kit
5-
extension/chromium/socratic-code/build
4+
chromium/socratic-code/node_modules
5+
chromium/socratic-code/.svelte-kit
6+
chromium/socratic-code/build

cmd/app/main.go

+28-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,33 @@
11
package main
22

3-
import "fmt"
3+
import (
4+
"log"
5+
"os"
6+
7+
"github.com/ary82/sleetcode/internal/infra"
8+
"github.com/joho/godotenv"
9+
)
410

511
func main() {
6-
fmt.Println("hello")
12+
mode := os.Getenv("MODE")
13+
if mode != "prod" {
14+
err := godotenv.Load("./.env")
15+
if err != nil {
16+
log.Fatal(err)
17+
}
18+
}
19+
20+
port := os.Getenv("PORT")
21+
if port == "" {
22+
log.Fatal("port envvar empty")
23+
}
24+
geminiKey := os.Getenv("GEMINI_KEY")
25+
if geminiKey == "" {
26+
log.Fatal("gemini_key envvar empty")
27+
}
28+
29+
err := infra.Run(port, geminiKey)
30+
if err != nil {
31+
log.Fatal(err)
32+
}
733
}

go.mod

+51
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,54 @@
11
module github.com/ary82/sleetcode
22

33
go 1.23.1
4+
5+
require (
6+
github.com/gofiber/fiber/v2 v2.52.5
7+
github.com/google/generative-ai-go v0.18.0
8+
github.com/joho/godotenv v1.5.1
9+
google.golang.org/api v0.186.0
10+
)
11+
12+
require (
13+
cloud.google.com/go v0.115.0 // indirect
14+
cloud.google.com/go/ai v0.8.0 // indirect
15+
cloud.google.com/go/auth v0.6.0 // indirect
16+
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
17+
cloud.google.com/go/compute/metadata v0.3.0 // indirect
18+
cloud.google.com/go/longrunning v0.5.7 // indirect
19+
github.com/andybalholm/brotli v1.0.5 // indirect
20+
github.com/felixge/httpsnoop v1.0.4 // indirect
21+
github.com/go-logr/logr v1.4.1 // indirect
22+
github.com/go-logr/stdr v1.2.2 // indirect
23+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
24+
github.com/golang/protobuf v1.5.4 // indirect
25+
github.com/google/s2a-go v0.1.7 // indirect
26+
github.com/google/uuid v1.6.0 // indirect
27+
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
28+
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
29+
github.com/klauspost/compress v1.17.0 // indirect
30+
github.com/mattn/go-colorable v0.1.13 // indirect
31+
github.com/mattn/go-isatty v0.0.20 // indirect
32+
github.com/mattn/go-runewidth v0.0.15 // indirect
33+
github.com/rivo/uniseg v0.2.0 // indirect
34+
github.com/valyala/bytebufferpool v1.0.0 // indirect
35+
github.com/valyala/fasthttp v1.51.0 // indirect
36+
github.com/valyala/tcplisten v1.0.0 // indirect
37+
go.opencensus.io v0.24.0 // indirect
38+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 // indirect
39+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect
40+
go.opentelemetry.io/otel v1.26.0 // indirect
41+
go.opentelemetry.io/otel/metric v1.26.0 // indirect
42+
go.opentelemetry.io/otel/trace v1.26.0 // indirect
43+
golang.org/x/crypto v0.24.0 // indirect
44+
golang.org/x/net v0.26.0 // indirect
45+
golang.org/x/oauth2 v0.21.0 // indirect
46+
golang.org/x/sync v0.7.0 // indirect
47+
golang.org/x/sys v0.21.0 // indirect
48+
golang.org/x/text v0.16.0 // indirect
49+
golang.org/x/time v0.5.0 // indirect
50+
google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4 // indirect
51+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4 // indirect
52+
google.golang.org/grpc v1.64.1 // indirect
53+
google.golang.org/protobuf v1.34.2 // indirect
54+
)

go.sum

+191
Large diffs are not rendered by default.

internal/infra/constants.go

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package infra
2+
3+
const PROMPT string = `You are a teacher helping a student in solving leetcode. The problem is "%v". The solution is in "%v" programming language. The student is attempting the solution and has currently a code of %v lines. The student is attempting the follwing solution:"%v". Help the student towards the solution using Socratic teaching questions. If you feel like their solution can be optimized further, give some questions according to the Socratic Teaching method philosophy and at what line of the solution they should be asked. The questions should be insightful and encourage the student towards getting the answer on their own. There should be a maximum of one question per line of code, and the questions should not excede the number of lines.`

internal/infra/domain.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package infra
2+
3+
import "fmt"
4+
5+
type TextGenerationRequest struct {
6+
Question string `json:"question"`
7+
Lines int `json:"lines"`
8+
Lang string `json:"lang"`
9+
Code string `json:"code"`
10+
}
11+
12+
type TextGenerationResponse struct {
13+
Line int `json:"line"`
14+
Q string `json:"q"`
15+
}
16+
17+
func (req *TextGenerationRequest) Validate() error {
18+
if req.Question == "" {
19+
return fmt.Errorf("no question given")
20+
}
21+
if req.Lines == 0 {
22+
return fmt.Errorf("0 lines solution not allowed")
23+
}
24+
if req.Lang == "" {
25+
return fmt.Errorf("lang cannot be empty")
26+
}
27+
if req.Code == "" {
28+
return fmt.Errorf("no code input given")
29+
}
30+
31+
return nil
32+
}

internal/infra/run.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package infra
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/gofiber/fiber/v2"
8+
"github.com/google/generative-ai-go/genai"
9+
"google.golang.org/api/option"
10+
)
11+
12+
func Run(port string, geminiKey string) error {
13+
app := fiber.New(fiber.Config{
14+
AppName: "Socratic code",
15+
ServerHeader: "Fiber",
16+
})
17+
18+
gClient, err := genai.NewClient(context.Background(), option.WithAPIKey(geminiKey))
19+
if err != nil {
20+
return err
21+
}
22+
textModel := gClient.GenerativeModel("gemini-1.5-flash")
23+
textModel.ResponseMIMEType = "application/json"
24+
textModel.ResponseSchema = &genai.Schema{
25+
Type: genai.TypeArray,
26+
Items: &genai.Schema{
27+
Type: genai.TypeObject,
28+
Properties: map[string]*genai.Schema{
29+
"line": {Type: genai.TypeInteger},
30+
"q": {Type: genai.TypeString},
31+
},
32+
},
33+
}
34+
35+
server := NewServer(app, textModel)
36+
37+
err = server.App.Listen(fmt.Sprintf(":%v", port))
38+
return err
39+
}

internal/infra/server.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package infra
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
8+
"github.com/gofiber/fiber/v2"
9+
"github.com/gofiber/fiber/v2/middleware/compress"
10+
"github.com/gofiber/fiber/v2/middleware/logger"
11+
"github.com/google/generative-ai-go/genai"
12+
)
13+
14+
type Server struct {
15+
App *fiber.App
16+
TextModel *genai.GenerativeModel
17+
}
18+
19+
func NewServer(app *fiber.App, textModel *genai.GenerativeModel) *Server {
20+
s := &Server{
21+
App: app,
22+
TextModel: textModel,
23+
}
24+
s.registerRoutes()
25+
return s
26+
}
27+
28+
func (s *Server) registerRoutes() {
29+
s.App.Use(logger.New())
30+
s.App.Use(compress.New())
31+
32+
s.App.Get("/", s.getSocraticQuestions)
33+
}
34+
35+
func (s *Server) getSocraticQuestions(ctx *fiber.Ctx) error {
36+
req := new(TextGenerationRequest)
37+
38+
err := ctx.BodyParser(req)
39+
if err != nil {
40+
return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
41+
"err": err.Error(),
42+
})
43+
}
44+
err = req.Validate()
45+
if err != nil {
46+
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
47+
"err": err.Error(),
48+
})
49+
}
50+
51+
dynamicPrompt := fmt.Sprintf(PROMPT, req.Question, req.Lang, req.Lines, req.Code)
52+
fmt.Println(dynamicPrompt)
53+
res, err := s.TextModel.GenerateContent(
54+
context.Background(),
55+
genai.Text(dynamicPrompt),
56+
)
57+
if err != nil {
58+
return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
59+
"error": err.Error(),
60+
})
61+
}
62+
63+
resJson := []TextGenerationResponse{}
64+
err = json.Unmarshal([]byte(res.Candidates[0].Content.Parts[0].(genai.Text)), &resJson)
65+
if err != nil {
66+
return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
67+
"error": err.Error(),
68+
})
69+
}
70+
71+
return ctx.Status(fiber.StatusOK).JSON(resJson)
72+
}

0 commit comments

Comments
 (0)