Imported from hnatekmarorg/lmproxy (
AGENTS.md). Install upstream withnpx skills add hnatekmarorg/lmproxy. Copyright stays with the author.
Agent Guidelines for LLM Proxy
This document provides guidelines for LLM agents working on the LLM Proxy codebase.
Table of Contents
- Project Overview
- Codebase Structure
- Agent Task Guidelines
- Code Patterns
- Testing Guidelines
- Common Tasks
Project Overview
What this project does:
- Lightweight HTTP proxy for routing requests to multiple LLM endpoints
- Merges client requests with configured default model parameters
- Supports SSE streaming for real-time responses
- Path-based routing to different backend models
Key technologies:
- Go (Golang)
- YAML configuration
- HTTP/SSE handling
Codebase Structure
.
├── main.go # Entry point: arg parsing, config loading, server startup
├── config/
│ └── models.go # Configuration types, YAML unmarshaling, validation
├── proxy/
│ ├── proxy.go # Core proxy logic and request handling
│ ├── router.go # Path and body-based routing
│ ├── request.go # Request body preparation and merging
│ ├── response.go # SSE streaming and response forwarding
│ ├── headers.go # Header forwarding
│ ├── models_handler.go # /v1/models endpoint handler
│ └── uuid.go # Request ID generation
├── util/
│ └── map.go # Map merging, deep copy, utility functions
├── cmd/install/
│ └── main.go # Interactive setup wizard
├── charts/
│ └── lm-proxy/ # Kubernetes Helm chart
├── config.yaml # Example configuration
└── AGENTS.md # This file - guidelines for AI agents
File Responsibilities
| File | Responsibility | Agent Notes |
|---|---|---|
main.go |
Entry point, lifecycle | Don't change unless adding CLI flags |
config/models.go |
Config parsing, types | Safe to extend with new config fields |
proxy/proxy.go |
Core proxy logic | Be careful with routing/merging changes |
proxy/router.go |
Request routing | Path + body-based model routing |
proxy/request.go |
Body preparation | Request body merging with defaults |
proxy/response.go |
Response streaming | SSE and regular response forwarding |
proxy/models_handler.go |
Model discovery | /v1/models endpoint handler |
util/map.go |
Shared utilities | Map merging helpers |
Agent Task Guidelines
When Adding New Features
- Read existing code first - Understand the patterns before writing
- Follow existing conventions - Match naming, style, and structure
- Add tests - Include unit tests for new functionality
- Update documentation - Update CONFIG.md if adding config fields
When Fixing Bugs
- Reproduce the issue - Understand the root cause before fixing
- Add a test case - Ensure the bug doesn't regress
- Make minimal changes - Fix only what's broken
- Check for side effects - Verify no other code depends on the broken behavior
When Refactoring
- Run tests first - Establish baseline
- Refactor in small steps - Commit each logical change separately
- Verify tests pass - After each refactoring step
- Don't change behavior - Refactoring ≠ feature changes
Code Patterns
Configuration Structure
// Server configuration
type Server struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
}
// Endpoint configuration
type Endpoint struct {
Host string `yaml:"host"`
Models []Model `yaml:"models"`
}
// Model configuration
type Model struct {
ID string `yaml:"id"`
Path string `yaml:"path"`
Body map[string]any `yaml:"body"`
ExtraBody map[string]any `yaml:"extra_body"`
ChatTemplateKwargs map[string]any `yaml:"chat_template_kwargs"`
}
Request Handling Pattern
// 1. Parse incoming request
// 2. Resolve target endpoint based on path
// 3. Merge config defaults with client request
// 4. Forward to backend server
// 5. Stream response back to client
Error Handling Pattern
// HTTP status codes to use:
// 400 - Invalid request or configuration
// 404 - Unknown route or model
// 500 - Internal proxy error
// 502 - Backend server error
Map Merging Pattern (in util/util.go)
// Deep merge maps for config + request body
// Client request takes precedence over defaults
// Handle nested maps recursively
Testing Guidelines
Test File Naming
main_test.go- Tests for main.goconfig/models_test.go- Tests for config packageproxy/config_test.go- Tests for proxy packageutil/util_test.go- Tests for util package
Test Coverage Priorities
- Configuration loading - Test YAML parsing and validation
- Request routing - Test path resolution to endpoints
- Body merging - Test config + request merging logic
- Error handling - Test all error paths
Example Test Structure
func TestLoadConfig(t *testing.T) {
// 1. Setup: Create test config file
// 2. Execute: Load config
// 3. Verify: Check all fields parsed correctly
// 4. Cleanup: Remove temp file
}
func TestRouteRequest(t *testing.T) {
// Test path resolution to correct endpoint
}
func TestMergeBody(t *testing.T) {
// Test config defaults + client request merging
}
Common Tasks
Task: Add a New Configuration Field
- Add field to the appropriate struct in
config/models.go - Add YAML tag with field name
- Add default value if needed
- Add validation if required
- Update CONFIG.md documentation
- Add test case for the new field
Task: Add a New Utility Function
- Check if similar function exists in
util/util.go - Add function with clear name and documentation
- Add unit tests in
util/util_test.go - Use in target code
- Verify all tests pass
Task: Fix a Routing Bug
- Read
proxy/config.goto understand routing logic - Identify the bug (path matching, endpoint resolution, etc.)
- Write failing test case first
- Fix the routing logic
- Verify test passes
- Check for similar patterns elsewhere
Task: Add Logging Configuration Support
- Add
Loggingstruct toconfig/models.go:type Logging struct { Level string `yaml:"level"` Format string `yaml:"format"` } - Add
Loggingfield to main config struct - Add default values (level: "info", format: "text")
- Update proxy to use logging config
- Update CONFIG.md with new fields
Task: Add Timeout Configuration
- Add
Timeoutfield to main config struct inconfig/models.go - Add default value (30 seconds)
- Apply timeout to HTTP client in proxy
- Update CONFIG.md
- Add test for timeout behavior
Architecture Notes
Request Flow
Client Request
↓
Path Resolution — Matches URL prefix to model path (proxy/router.go)
↓ (if no match)
Body-Based Resolution — Reads "model" field from POST body (proxy/router.go)
↓
Config Lookup — Finds endpoint + model config (config/models.go)
↓
Body Merging — Merges config defaults with client request (util/map.go)
↓
Backend Forwarding — Proxies request to LLM server (proxy/proxy.go)
↓
Response Streaming — Streams response back to client (proxy/response.go)
Key Design Decisions
- Path-based routing — Routes matched by URL prefix (primary)
- Body-based routing — Fallback reads
modelfield from POST body for pathless models - Config merging — Defaults merged with client request (client wins)
- SSE support — Streaming responses forwarded as-is
- Model discovery —
GET /v1/modelsreturns all configured models in OpenAI format - No auth layer — Proxy doesn't add authentication
Things to Be Careful About
⚠️ Don't change the config YAML structure without updating:
- All config structs
- Default values
- Validation logic
- CONFIG.md documentation
⚠️ Don't break backward compatibility - Existing config files should still work
⚠️ Don't introduce blocking I/O - Keep the server non-blocking for concurrency
Quick Reference
Common Commands
# Run all tests
go test -v ./...
# Run tests with coverage
go test -v -cover ./...
# Build the binary
go build -o lmproxy main.go
# Run with config
./lmproxy config.yaml
Files to Read for Common Tasks
| Task | Read First |
|---|---|
| Add config field | config/models.go |
| Fix routing bug | proxy/router.go |
| Add API endpoint | proxy/proxy.go + proxy/models_handler.go |
| Add utility function | util/map.go |
| Change CLI behavior | main.go |
| Understand request flow | proxy/proxy.go + proxy/router.go + main.go |
See Also
- README.md - User-facing documentation
- CONFIG.md - Configuration reference
- config.yaml - Example configuration