Implement remaining code review suggestions

- Add graceful shutdown with SIGTERM/SIGINT handling\n- Make model list configurable via config.yaml\n- Remove sensitive data from debug logs (log sizes instead of full bodies)\n- Handle array format in system messages (both string and []interface{})\n- Update config.yaml with new models structure
This commit is contained in:
Franz Kafka 2026-04-15 09:16:44 +00:00
parent f65ad6b189
commit cea246da83
5 changed files with 60 additions and 12 deletions

View file

@ -1,2 +1,8 @@
port: 8080
upstream_url: "https://api.z.ai/api/anthropic"
models:
- id: "glm-4.7"
owned_by: "zhipu"
- id: "glm-4.6"
owned_by: "zhipu"

View file

@ -53,8 +53,18 @@ func extractSystemMessage(messages []Message) (string, []Message) {
for _, msg := range messages {
if msg.Role == "system" {
if content, ok := msg.Content.(string); ok {
switch content := msg.Content.(type) {
case string:
systemParts = append(systemParts, content)
case []interface{}:
// Extract text from content array
for _, part := range content {
if partMap, ok := part.(map[string]interface{}); ok {
if text, ok := partMap["text"].(string); ok {
systemParts = append(systemParts, text)
}
}
}
}
} else {
rest = append(rest, msg)

View file

@ -11,10 +11,16 @@ import (
"time"
)
type ModelConfig struct {
ID string `yaml:"id"`
OwnedBy string `yaml:"owned_by"`
}
// Config holds the application configuration
type Config struct {
Port int `yaml:"port"`
UpstreamURL string `yaml:"upstream_url"`
Models []ModelConfig `yaml:"models"`
}
var config *Config
@ -56,9 +62,14 @@ func handleModels(w http.ResponseWriter, r *http.Request) {
return
}
models := []map[string]interface{}{
{"id": "glm-4.7", "object": "model", "created": 1234567890, "owned_by": "zhipu"},
{"id": "glm-4.6", "object": "model", "created": 1234567890, "owned_by": "zhipu"},
models := make([]map[string]interface{}, len(config.Models))
for i, model := range config.Models {
models[i] = map[string]interface{}{
"id": model.ID,
"object": "model",
"created": 1234567890,
"owned_by": model.OwnedBy,
}
}
response := map[string]interface{}{
@ -117,7 +128,7 @@ func handleChatCompletions(w http.ResponseWriter, r *http.Request) {
anthropicReq.Stream = false
reqBody, _ := json.Marshal(anthropicReq)
log.Printf("[debug] Sending to upstream %s, model=%s, body=%s", config.UpstreamURL, req.Model, string(reqBody))
log.Printf("[debug] Sending to upstream %s, model=%s, body_size=%d bytes", config.UpstreamURL, req.Model, len(reqBody))
// Non-streaming request to upstream
upstreamResp, err := callUpstream(anthropicReq, apiKey, sessionID)
@ -129,7 +140,7 @@ func handleChatCompletions(w http.ResponseWriter, r *http.Request) {
if upstreamResp.StatusCode != http.StatusOK {
respBody, _ := io.ReadAll(upstreamResp.Body)
log.Printf("[debug] Upstream error status %d: %s", upstreamResp.StatusCode, string(respBody))
log.Printf("[debug] Upstream error status %d, body_size=%d bytes", upstreamResp.StatusCode, len(respBody))
writeError(w, http.StatusBadGateway, fmt.Sprintf("Upstream returned error: %s", string(respBody)), "upstream_error", fmt.Sprintf("status_%d", upstreamResp.StatusCode))
return
}
@ -140,7 +151,7 @@ func handleChatCompletions(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusBadGateway, "Failed to read upstream response", "upstream_error", "body_read_error")
return
}
log.Printf("[debug] Upstream response: %s", string(respBody))
log.Printf("[debug] Upstream response received, body_size=%d bytes", len(respBody))
var anthropicResp AnthropicResponse
if err := json.Unmarshal(respBody, &anthropicResp); err != nil {

23
main.go
View file

@ -6,6 +6,9 @@ import (
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/google/uuid"
"gopkg.in/yaml.v3"
@ -38,10 +41,28 @@ func main() {
})
addr := fmt.Sprintf(":%d", config.Port)
server := &http.Server{Addr: addr}
go func() {
log.Printf("Starting proxx on %s, upstream: %s", addr, config.UpstreamURL)
if err := http.ListenAndServe(addr, nil); err != nil {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server failed: %v", err)
}
}()
// Wait for interrupt signal
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
log.Println("Shutting down server...")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Printf("Server shutdown error: %v", err)
}
log.Println("Server stopped")
}
// contextKey is a custom type for context keys

BIN
proxx

Binary file not shown.