Skip to content

Commit fd74714

Browse files
committed
Merge branch 'optimize/ai-gateway-round2-20260619'
AI Gateway round 2 optimizations: - perf: parallelize RAG List calls with errgroup (5 calls → concurrent) - perf: add chat history limit (max 40 messages) to prevent unbounded growth
2 parents f8a97ff + b1d899f commit fd74714

4 files changed

Lines changed: 46 additions & 51 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ require (
1414
go.opentelemetry.io/otel/sdk v1.43.0
1515
go.uber.org/goleak v1.3.0
1616
golang.org/x/net v0.55.0
17-
golang.org/x/sync v0.20.0
17+
golang.org/x/sync v0.21.0
1818
google.golang.org/grpc v1.80.0
1919
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af
2020
gopkg.in/yaml.v3 v3.0.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
149149
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
150150
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
151151
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
152-
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
153-
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
152+
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
153+
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
154154
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
155155
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
156156
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=

internal/admin/chatbot/rag.go

Lines changed: 38 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"strings"
77

88
corev1 "k8s.io/api/core/v1"
9+
"golang.org/x/sync/errgroup"
910
"sigs.k8s.io/controller-runtime/pkg/client"
1011
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
1112
)
@@ -20,15 +21,26 @@ import (
2021
// - HTTPRoutes and GRPCRoutes (namespace/name, rules, backendRefs).
2122
// - Available Services (namespace/name, ports).
2223
func BuildRAGContext(ctx context.Context, cl client.Client, controllerName string) (string, error) {
23-
var b strings.Builder
24-
25-
// 1. GatewayClasses controlled by this controller.
26-
gcList := &gatewayv1.GatewayClassList{}
27-
if err := cl.List(ctx, gcList); err != nil {
28-
return "", fmt.Errorf("rag: list GatewayClasses: %w", err)
24+
var (
25+
gcList gatewayv1.GatewayClassList
26+
gwList gatewayv1.GatewayList
27+
httpList gatewayv1.HTTPRouteList
28+
grpcList gatewayv1.GRPCRouteList
29+
svcList corev1.ServiceList
30+
)
31+
32+
g, ctx := errgroup.WithContext(ctx)
33+
g.Go(func() error { return cl.List(ctx, &gcList) })
34+
g.Go(func() error { return cl.List(ctx, &gwList) })
35+
g.Go(func() error { return cl.List(ctx, &httpList) })
36+
g.Go(func() error { return cl.List(ctx, &grpcList) })
37+
g.Go(func() error { return cl.List(ctx, &svcList) })
38+
39+
if err := g.Wait(); err != nil {
40+
return "", fmt.Errorf("rag: list resources: %w", err)
2941
}
3042

31-
managedClasses := make(map[string]bool)
43+
managedClasses := make(map[string]bool, len(gcList.Items))
3244
for _, gc := range gcList.Items {
3345
if string(gc.Spec.ControllerName) == controllerName {
3446
managedClasses[gc.Name] = true
@@ -39,39 +51,17 @@ func BuildRAGContext(ctx context.Context, cl client.Client, controllerName strin
3951
return "No managed GatewayClasses found for controller " + controllerName, nil
4052
}
4153

42-
// 2. Gateways.
43-
gwList := &gatewayv1.GatewayList{}
44-
if err := cl.List(ctx, gwList); err != nil {
45-
return "", fmt.Errorf("rag: list Gateways: %w", err)
46-
}
47-
4854
var managedGateways []gatewayv1.Gateway
4955
for _, gw := range gwList.Items {
5056
if managedClasses[string(gw.Spec.GatewayClassName)] {
5157
managedGateways = append(managedGateways, gw)
5258
}
5359
}
5460

55-
// 3. HTTPRoutes.
56-
httpList := &gatewayv1.HTTPRouteList{}
57-
if err := cl.List(ctx, httpList); err != nil {
58-
return "", fmt.Errorf("rag: list HTTPRoutes: %w", err)
59-
}
60-
61-
// 4. GRPCRoutes.
62-
grpcList := &gatewayv1.GRPCRouteList{}
63-
if err := cl.List(ctx, grpcList); err != nil {
64-
return "", fmt.Errorf("rag: list GRPCRoutes: %w", err)
65-
}
66-
67-
// 5. Services.
68-
svcList := &corev1.ServiceList{}
69-
if err := cl.List(ctx, svcList); err != nil {
70-
return "", fmt.Errorf("rag: list Services: %w", err)
71-
}
72-
7361
// ── Format output ────────────────────────────────────────────
7462

63+
var b strings.Builder
64+
7565
b.WriteString("## Current Gateway API Topology\n\n")
7666

7767
// Gateways
@@ -182,36 +172,37 @@ func BuildRAGContext(ctx context.Context, cl client.Client, controllerName strin
182172
// Services
183173
b.WriteString("### Services\n\n")
184174
if len(svcList.Items) == 0 {
185-
b.WriteString("(none)\n")
175+
b.WriteString("(none)\n\n")
186176
} else {
187177
for _, svc := range svcList.Items {
188-
fmt.Fprintf(&b, "- **%s/%s**", svc.Namespace, svc.Name)
178+
fmt.Fprintf(&b, "- **%s/%s** (type=%s)", svc.Namespace, svc.Name, svc.Spec.Type)
189179
if len(svc.Spec.Ports) > 0 {
190-
ports := make([]string, 0, len(svc.Spec.Ports))
191-
for _, p := range svc.Spec.Ports {
192-
ports = append(ports, fmt.Sprintf("%s:%d/%s", p.Name, p.Port, p.Protocol))
180+
b.WriteString(" [")
181+
for j, port := range svc.Spec.Ports {
182+
if j > 0 {
183+
b.WriteString(", ")
184+
}
185+
fmt.Fprintf(&b, "%d/%s", port.Port, port.Protocol)
193186
}
194-
fmt.Fprintf(&b, " ports=[%s]", strings.Join(ports, ", "))
187+
b.WriteString("]")
195188
}
196189
b.WriteString("\n")
197190
}
191+
b.WriteString("\n")
198192
}
199193

200194
return b.String(), nil
201195
}
202196

203197
func fmtRouteParents(b *strings.Builder, refs []gatewayv1.ParentReference, defaultNS string) {
204-
if len(refs) == 0 {
205-
return
206-
}
207-
parts := make([]string, 0, len(refs))
208-
for _, pr := range refs {
198+
for j, ref := range refs {
199+
if j > 0 {
200+
b.WriteString(", ")
201+
}
209202
ns := defaultNS
210-
if pr.Namespace != nil {
211-
ns = string(*pr.Namespace)
203+
if ref.Namespace != nil {
204+
ns = string(*ref.Namespace)
212205
}
213-
parts = append(parts, fmt.Sprintf("%s/%s", ns, pr.Name))
206+
fmt.Fprintf(b, " → %s/%s", ns, ref.Name)
214207
}
215-
fmt.Fprintf(b, " → gateways: [%s]", strings.Join(parts, ", "))
216208
}
217-

internal/admin/server_chatbot.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
const (
1919
chatbotConfigNamespace = "nantian-gw"
2020
chatbotConfigSecret = "chatbot-config"
21+
maxChatHistoryMessages = 40
2122
)
2223

2324
// maskAPIKey returns a masked version of the API key for safe display.
@@ -202,8 +203,11 @@ func (s *Server) handleChatbotChat(w http.ResponseWriter, r *http.Request) {
202203
// Prep the system prompt with RAG context.
203204
systemPrompt := buildSystemPrompt(ragContext)
204205

205-
// Build the full history.
206+
// Build the full history (with a bound to prevent unbounded growth).
206207
history := append([]chatbot.Message(nil), req.History...)
208+
if len(history) > maxChatHistoryMessages {
209+
history = history[len(history)-maxChatHistoryMessages:]
210+
}
207211

208212
// Create the LLM adapter.
209213
llm := chatbot.NewOpenAIAdapter(cfg.APIEndpoint, cfg.APIKey, cfg.Model, cfg.Temperature)

0 commit comments

Comments
 (0)