Skip to content

Commit 19a654b

Browse files
committed
Added LLM routes
1 parent 73d9641 commit 19a654b

File tree

5 files changed

+225
-0
lines changed

5 files changed

+225
-0
lines changed

controllers/llm_controller.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package controllers
2+
3+
import (
4+
"io"
5+
"net/http"
6+
7+
"github.com/Thanus-Kumaar/controller_microservice_v2/modules"
8+
"github.com/rs/zerolog"
9+
"maps"
10+
)
11+
12+
// LlmController holds the dependencies for the llm proxy handlers.
13+
type LlmController struct {
14+
Module *modules.LlmModule
15+
Logger zerolog.Logger
16+
}
17+
18+
// NewLlmController creates and returns a new LlmController.
19+
func NewLlmController(module *modules.LlmModule, logger zerolog.Logger) *LlmController {
20+
return &LlmController{
21+
Module: module,
22+
Logger: logger,
23+
}
24+
}
25+
26+
// GenerateNotebookHandler handles POST /api/v1/llm/generate
27+
func (c *LlmController) GenerateNotebookHandler(w http.ResponseWriter, r *http.Request) {
28+
ctx := r.Context()
29+
30+
resp, err := c.Module.GenerateNotebook(ctx, r.Body)
31+
if err != nil {
32+
c.Logger.Error().Err(err).Msg("Failed to proxy generate request")
33+
http.Error(w, err.Error(), http.StatusInternalServerError)
34+
return
35+
}
36+
defer resp.Body.Close()
37+
38+
// Copy headers from the proxied response to our response
39+
maps.Copy(w.Header(), resp.Header)
40+
41+
// Write the status code from the proxied response
42+
w.WriteHeader(resp.StatusCode)
43+
44+
// Stream the body from the proxied response
45+
if _, err := io.Copy(w, resp.Body); err != nil {
46+
c.Logger.Error().Err(err).Msg("Failed to stream response body")
47+
}
48+
}
49+
50+
// ModifyNotebookHandler handles POST /api/v1/llm/sessions/{session_id}/modify
51+
func (c *LlmController) ModifyNotebookHandler(w http.ResponseWriter, r *http.Request) {
52+
sessionID := r.PathValue("session_id")
53+
ctx := r.Context()
54+
55+
resp, err := c.Module.ModifyNotebook(ctx, sessionID, r.Body)
56+
if err != nil {
57+
c.Logger.Error().Err(err).Msgf("Failed to proxy modify request for session %s", sessionID)
58+
http.Error(w, err.Error(), http.StatusInternalServerError)
59+
return
60+
}
61+
defer resp.Body.Close()
62+
63+
maps.Copy(w.Header(), resp.Header)
64+
w.WriteHeader(resp.StatusCode)
65+
66+
if _, err := io.Copy(w, resp.Body); err != nil {
67+
c.Logger.Error().Err(err).Msg("Failed to stream response body")
68+
}
69+
}
70+
71+
// FixNotebookHandler handles POST /api/v1/llm/sessions/{session_id}/fix
72+
func (c *LlmController) FixNotebookHandler(w http.ResponseWriter, r *http.Request) {
73+
sessionID := r.PathValue("session_id")
74+
ctx := r.Context()
75+
76+
resp, err := c.Module.FixNotebook(ctx, sessionID, r.Body)
77+
if err != nil {
78+
c.Logger.Error().Err(err).Msgf("Failed to proxy fix request for session %s", sessionID)
79+
http.Error(w, err.Error(), http.StatusInternalServerError)
80+
return
81+
}
82+
defer resp.Body.Close()
83+
84+
maps.Copy(w.Header(), resp.Header)
85+
w.WriteHeader(resp.StatusCode)
86+
87+
if _, err := io.Copy(w, resp.Body); err != nil {
88+
c.Logger.Error().Err(err).Msg("Failed to stream response body")
89+
}
90+
}

db/repository/llm_proxy.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package repository
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
)
9+
10+
// llmProxy implements the LlmRepository interface by acting as a proxy to the Python LLM service.
11+
type llmProxy struct {
12+
BaseURL string
13+
Client *http.Client
14+
}
15+
16+
type LlmRepository interface {
17+
GenerateNotebook(ctx context.Context, body io.Reader) (*http.Response, error)
18+
ModifyNotebook(ctx context.Context, sessionID string, body io.Reader) (*http.Response, error)
19+
FixNotebook(ctx context.Context, sessionID string, body io.Reader) (*http.Response, error)
20+
}
21+
22+
// NewLlmProxy creates a new llmProxy.
23+
func NewLlmProxy(baseURL string) LlmRepository {
24+
return &llmProxy{
25+
BaseURL: baseURL,
26+
Client: &http.Client{},
27+
}
28+
}
29+
30+
// GenerateNotebook proxies the request to the /generate endpoint of the LLM service.
31+
func (p *llmProxy) GenerateNotebook(ctx context.Context, body io.Reader) (*http.Response, error) {
32+
targetURL := fmt.Sprintf("%s/v1/generate", p.BaseURL)
33+
34+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, targetURL, body)
35+
if err != nil {
36+
return nil, fmt.Errorf("failed to create generate request: %w", err)
37+
}
38+
req.Header.Set("Content-Type", "application/json")
39+
req.Header.Set("Accept", "application/json")
40+
41+
resp, err := p.Client.Do(req)
42+
if err != nil {
43+
return nil, fmt.Errorf("failed to call generate endpoint: %w", err)
44+
}
45+
46+
return resp, nil
47+
}
48+
49+
// ModifyNotebook proxies the request to the /sessions/{session_id}/modify endpoint.
50+
func (p *llmProxy) ModifyNotebook(ctx context.Context, sessionID string, body io.Reader) (*http.Response, error) {
51+
targetURL := fmt.Sprintf("%s/v1/sessions/%s/modify", p.BaseURL, sessionID)
52+
53+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, targetURL, body)
54+
if err != nil {
55+
return nil, fmt.Errorf("failed to create modify request: %w", err)
56+
}
57+
req.Header.Set("Content-Type", "application/json")
58+
req.Header.Set("Accept", "application/json")
59+
60+
resp, err := p.Client.Do(req)
61+
if err != nil {
62+
return nil, fmt.Errorf("failed to call modify endpoint: %w", err)
63+
}
64+
65+
return resp, nil
66+
}
67+
68+
// FixNotebook proxies the request to the /sessions/{session_id}/fix endpoint.
69+
func (p *llmProxy) FixNotebook(ctx context.Context, sessionID string, body io.Reader) (*http.Response, error) {
70+
targetURL := fmt.Sprintf("%s/v1/sessions/%s/fix", p.BaseURL, sessionID)
71+
72+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, targetURL, body)
73+
if err != nil {
74+
return nil, fmt.Errorf("failed to create fix request: %w", err)
75+
}
76+
req.Header.Set("Content-Type", "application/json")
77+
req.Header.Set("Accept", "application/json")
78+
79+
resp, err := p.Client.Do(req)
80+
if err != nil {
81+
return nil, fmt.Errorf("failed to call fix endpoint: %w", err)
82+
}
83+
84+
return resp, nil
85+
}

docker-compose.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ services:
1616
CULL_INTERVAL_MINUTES: 10
1717
IDLE_THRESHOLD_MINUTES: 30
1818
AUTH_GRPC_SERVER_ADDRESS: "localhost:5001"
19+
LLM_MICROSERVICE_URL: "http://host.docker.internal:8000"
1920
networks:
2021
- evoc-net
2122
depends_on:

modules/llm_module.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package modules
2+
3+
import (
4+
"context"
5+
"io"
6+
"net/http"
7+
8+
"github.com/Thanus-Kumaar/controller_microservice_v2/db/repository"
9+
)
10+
11+
// LlmModule encapsulates the business logic for proxying requests to the LLM service.
12+
type LlmModule struct {
13+
Repo repository.LlmRepository
14+
}
15+
16+
// NewLlmModule creates and returns a new LlmModule.
17+
func NewLlmModule(repo repository.LlmRepository) *LlmModule {
18+
return &LlmModule{
19+
Repo: repo,
20+
}
21+
}
22+
23+
// GenerateNotebook calls the repository to proxy the generate request.
24+
func (m *LlmModule) GenerateNotebook(ctx context.Context, body io.Reader) (*http.Response, error) {
25+
return m.Repo.GenerateNotebook(ctx, body)
26+
}
27+
28+
// ModifyNotebook calls the repository to proxy the modify request.
29+
func (m *LlmModule) ModifyNotebook(ctx context.Context, sessionID string, body io.Reader) (*http.Response, error) {
30+
return m.Repo.ModifyNotebook(ctx, sessionID, body)
31+
}
32+
33+
// FixNotebook calls the repository to proxy the fix request.
34+
func (m *LlmModule) FixNotebook(ctx context.Context, sessionID string, body io.Reader) (*http.Response, error) {
35+
return m.Repo.FixNotebook(ctx, sessionID, body)
36+
}

routes/api.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package routes
22

33
import (
44
"net/http"
5+
"os"
56

67
"github.com/Thanus-Kumaar/controller_microservice_v2/pkg"
78
"github.com/Thanus-Kumaar/controller_microservice_v2/db/repository"
@@ -19,6 +20,12 @@ func RegisterAPIRoutes(mux *http.ServeMux, c *jupyterclient.Client) { // Updated
1920
sessionController := controllers.NewSessionController(sessionModule, *pkg.Logger)
2021

2122
problemRepo := repository.NewProblemRepository(db.Pool)
23+
24+
llmServiceURL := os.Getenv("LLM_MICROSERVICE_URL")
25+
llmRepo := repository.NewLlmProxy(llmServiceURL)
26+
llmModule := modules.NewLlmModule(llmRepo)
27+
llmController := controllers.NewLlmController(llmModule, *pkg.Logger)
28+
2229
problemModule := modules.NewProblemModule(problemRepo)
2330
problemController := controllers.NewProblemController(problemModule, *pkg.Logger)
2431

@@ -69,6 +76,12 @@ func RegisterAPIRoutes(mux *http.ServeMux, c *jupyterclient.Client) { // Updated
6976
mux.HandleFunc("GET /api/v1/cells/{cell_id}/outputs", cellController.GetCellOutputsByCellIDHandler)
7077
mux.HandleFunc("DELETE /api/v1/outputs/{output_id}", cellController.DeleteCellOutputHandler)
7178

79+
80+
// Llm Routes
81+
mux.HandleFunc("POST /api/v1/llm/generate", llmController.GenerateNotebookHandler)
82+
mux.HandleFunc("POST /api/v1/llm/sessions/{session_id}/modify", llmController.ModifyNotebookHandler)
83+
mux.HandleFunc("POST /api/v1/llm/sessions/{session_id}/fix", llmController.FixNotebookHandler)
84+
7285
// Kernel Routes
7386
mux.HandleFunc("POST /api/v1/kernels", kernelController.StartKernelHandler)
7487
mux.HandleFunc("GET /api/v1/kernels", kernelController.ListKernelsHandler)

0 commit comments

Comments
 (0)