feat(ui): add front-page stats#4
Conversation
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Policy Check Failed✗ 3/3 policy checks failed: • Need 2 more approval(s) (0/2) — comment LGTM or approve via review To merge this PR:
|
PR SummaryWhat Changed
Key Changes by AreaMetrics & Monitoring: Added middleware to track requests, a metrics service with pruning, and Prometheus export via OpenTelemetry. Backend CPU/memory monitoring is now available. Files Changed
Review Focus Areas
ArchitectureDesign Decisions: Metrics are stored in-memory with configurable limits and periodic pruning to prevent unbounded growth. Prometheus export uses OpenTelemetry for compatibility with existing tooling. Backend monitoring samples resource usage without modifying backend processes. Scalability & Extensibility: Metrics service supports time-series queries and breakdowns by model/backend. Backend monitoring is lightweight and non-intrusive. Out-of-scope: persistent storage or distributed aggregation. Risks: In-memory metrics may be lost on restart (intentional for simplicity). The chart color array issue (review finding) is unintentional and should be fixed. Missing Merge StatusNOT MERGEABLE — PR Score 3/100, below threshold (50)
|
Greptile SummaryThis PR redesigns the LocalAI home page into a split dashboard/welcome experience and introduces an in-memory metrics store backed by a new middleware and five API endpoints that power live Chart.js charts showing request counts, endpoint usage, and model usage.
Confidence Score: 3/5The UI and settings page changes are safe to merge, but the core feature — live API stats on the dashboard — will not work because the metrics middleware cannot intercept the existing API routes. The middleware registration happens after the three main API route groups in app.go, so no real API request will ever be counted. Every chart on the new dashboard will display 'No data yet' regardless of actual traffic. The structural changes (new settings page, redesigned home page, new routes) are independently correct, but the feature the PR is named for is broken at the wiring layer. Two additional robustness concerns — an unguarded metrics-reset endpoint and a double-close risk in Stop() — sit on top of that central defect. core/http/app.go needs the metricsStore initialization and router.Use() call moved above the RegisterElevenLabsRoutes/RegisterLocalAIRoutes/RegisterOpenAIRoutes calls. core/http/routes/ui_api.go needs an auth check on the reset endpoint. core/services/metrics.go needs a sync.Once guard in Stop(). Important Files Changed
Sequence DiagramsequenceDiagram
participant Client
participant Fiber Router
participant ElevenLabs/LocalAI/OpenAI Routes
participant MetricsMiddleware
participant InMemoryMetricsStore
participant Dashboard
Note over Fiber Router: Registration order in app.go
Note over Fiber Router: 1. ElevenLabs/LocalAI/OpenAI routes registered
Note over Fiber Router: 2. MetricsMiddleware registered (too late!)
Note over Fiber Router: 3. UI API + UI routes registered
Client->>Fiber Router: POST /v1/chat/completions
Fiber Router->>ElevenLabs/LocalAI/OpenAI Routes: Matched first (registered earlier)
ElevenLabs/LocalAI/OpenAI Routes-->>Client: Response (no c.Next() call)
Note over MetricsMiddleware: Never reached
Client->>Fiber Router: GET /api/metrics/summary
Fiber Router->>MetricsMiddleware: Runs (UI routes registered after)
MetricsMiddleware->>InMemoryMetricsStore: GetTotalRequests(), GetEndpointStats()...
InMemoryMetricsStore-->>MetricsMiddleware: All zeros (no data recorded)
MetricsMiddleware-->>Client: "{totalRequests: 0, ...}"
Client->>Fiber Router: GET /
Fiber Router->>Dashboard: Render dashboard
Dashboard-->>Client: Charts showing empty data
|
| if !application.ApplicationConfig().DisableWebUI { | ||
|
|
||
| // Create metrics store for tracking usage (before API routes registration) | ||
| metricsStore := services.NewInMemoryMetricsStore() | ||
|
|
||
| // Add metrics middleware BEFORE API routes so it can intercept them | ||
| router.Use(middleware.MetricsMiddleware(metricsStore)) | ||
|
|
||
| // Register cleanup on shutdown | ||
| router.Hooks().OnShutdown(func() error { | ||
| metricsStore.Stop() | ||
| log.Info().Msg("Metrics store stopped") | ||
| return nil | ||
| }) | ||
|
|
||
| // Create opcache for tracking UI operations | ||
| opcache := services.NewOpCache(application.GalleryService()) | ||
| routes.RegisterUIAPIRoutes(router, application.ModelConfigLoader(), application.ApplicationConfig(), application.GalleryService(), opcache) | ||
| routes.RegisterUIAPIRoutes(router, application.ModelConfigLoader(), application.ApplicationConfig(), application.GalleryService(), opcache, metricsStore) | ||
| routes.RegisterUIRoutes(router, application.ModelConfigLoader(), application.ModelLoader(), application.ApplicationConfig(), application.GalleryService()) |
There was a problem hiding this comment.
Metrics middleware registered after API routes — will never fire
In Fiber v2, routes are matched in registration order. The MetricsMiddleware is added via router.Use() at line 214, but RegisterElevenLabsRoutes, RegisterLocalAIRoutes, and RegisterOpenAIRoutes are all registered at lines 204–206 — before the middleware. When a request arrives for /v1/chat/completions, Fiber matches the OpenAI handler first; that handler returns a response without calling c.Next(), so the metrics middleware is never reached. The comment on line 213 even acknowledges this must run "BEFORE API routes," but the placement is after them. The result is that the dashboard will always show 0 total requests and empty endpoint/model charts, making the entire feature non-functional. The metricsStore initialization block needs to move to before the three Register*Routes calls.
| func (m *InMemoryMetricsStore) Stop() { | ||
| close(m.stopChan) | ||
| } |
There was a problem hiding this comment.
close(m.stopChan) will panic with "close of closed channel" if called more than once. Fiber's OnShutdown hook is the only registered caller today, but there is no guard preventing a second invocation (e.g., if the hook fires twice during ungraceful shutdown, or a future caller adds another cleanup path). Wrapping the close in a sync.Once is the idiomatic Go protection for this pattern.
| </div> | ||
|
|
||
| <!-- Chart.js --> | ||
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> |
There was a problem hiding this comment.
Chart.js is loaded from a CDN without a Subresource Integrity (SRI) hash. If the CDN is compromised or the package is updated unexpectedly, malicious JavaScript could be served to every user who opens the dashboard. Adding the
integrity and crossorigin attributes pins the exact bytes that are allowed to execute.
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js" integrity="sha256-oVuCFqrvkPCjkEiMGj3eaEJHXjFHNOa8CMEF8z1KFMY=" crossorigin="anonymous"></script> |
| // Add metrics middleware BEFORE API routes so it can intercept them | ||
| router.Use(middleware.MetricsMiddleware(metricsStore)) |
There was a problem hiding this comment.
The metrics middleware is registered after API routes (RegisterElevenLabsRoutes, RegisterLocalAIRoutes, RegisterOpenAIRoutes) are already added to the router, so it will not intercept those routes despite the comment claiming it runs 'BEFORE API routes'.
Also reported at: core/http/app.go L208–L221
Suggested fix
// Create metrics store for tracking usage (before API routes registration)
metricsStore := services.NewInMemoryMetricsStore()
// Add metrics middleware BEFORE API routes so it can intercept them
router.Use(middleware.MetricsMiddleware(metricsStore))
// Register cleanup on shutdown
router.Hooks().OnShutdown(func() error {
metricsStore.Stop()
log.Info().Msg("Metrics store stopped")
return nil
})
// Health Checks should always be exempt from auth, so register these first
routes.HealthRoutes(router)
routes.RegisterElevenLabsRoutes(...)
routes.RegisterLocalAIRoutes(...)
routes.RegisterOpenAIRoutes(...)Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/app.go
Lines: 213-214
Issue Type: functional-high
Severity: high
Issue Description:
The metrics middleware is registered after API routes (RegisterElevenLabsRoutes, RegisterLocalAIRoutes, RegisterOpenAIRoutes) are already added to the router, so it will not intercept those routes despite the comment claiming it runs 'BEFORE API routes'.
_Also reported at: `core/http/app.go` L208–L221_
Current Code:
// Add metrics middleware BEFORE API routes so it can intercept them
router.Use(middleware.MetricsMiddleware(metricsStore))
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| app.Post("/api/metrics/reset", func(c *fiber.Ctx) error { | ||
| metricsStore.Reset() | ||
| return c.JSON(fiber.Map{ | ||
| "success": true, | ||
| "message": "Metrics reset successfully", | ||
| }) | ||
| }) |
There was a problem hiding this comment.
The /api/metrics/reset endpoint is a state-mutating POST route with no authentication, authorization check, or CSRF protection guard beyond whatever global middleware applies, allowing any user or script with network access to silently wipe all metrics data.
Suggested fix
// Reset metrics - restrict to admin/local access only
app.Post("/api/metrics/reset", func(c *fiber.Ctx) error {
// Guard: only allow if caller has admin privileges or remove endpoint entirely in production
if !appConfig.Debug {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
"error": "endpoint disabled in production",
})
}
metricsStore.Reset()
return c.JSON(fiber.Map{
"success": true,
"message": "Metrics reset successfully",
})
})Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/routes/ui_api.go
Lines: 811-817
Issue Type: security-high
Severity: high
Issue Description:
The `/api/metrics/reset` endpoint is a state-mutating POST route with no authentication, authorization check, or CSRF protection guard beyond whatever global middleware applies, allowing any user or script with network access to silently wipe all metrics data.
Current Code:
// Reset metrics (optional - for testing/admin purposes)
app.Post("/api/metrics/reset", func(c *fiber.Ctx) error {
metricsStore.Reset()
return c.JSON(fiber.Map{
"success": true,
"message": "Metrics reset successfully",
})
})
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| func (m *InMemoryMetricsStore) Stop() { | ||
| close(m.stopChan) | ||
| } |
There was a problem hiding this comment.
Stop() closes stopChan without guarding against double-close; calling Stop() twice will panic with 'close of closed channel'.
Suggested fix
func (m *InMemoryMetricsStore) Stop() {
m.mu.Lock()
defer m.mu.Unlock()
select {
case <-m.stopChan:
// already closed
default:
close(m.stopChan)
}
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 168-170
Issue Type: robustness-high
Severity: high
Issue Description:
Stop() closes stopChan without guarding against double-close; calling Stop() twice will panic with 'close of closed channel'.
Current Code:
func (m *InMemoryMetricsStore) Stop() {
close(m.stopChan)
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| if len(newTimeSeries) > m.maxRecords { | ||
| // Keep the most recent maxRecords entries | ||
| newTimeSeries = newTimeSeries[len(newTimeSeries)-m.maxRecords:] | ||
| log.Warn(). | ||
| Int("dropped", len(m.timeSeries)-len(newTimeSeries)). | ||
| Int("kept", len(newTimeSeries)). | ||
| Msg("Metrics store exceeded maximum records, dropping oldest entries") | ||
| } |
There was a problem hiding this comment.
The 'dropped' count in the log message is computed after newTimeSeries has already been re-sliced, so len(m.timeSeries)-len(newTimeSeries) compares the original full slice against the already-truncated slice, producing an incorrect (inflated) dropped count.
Suggested fix
if len(newTimeSeries) > m.maxRecords {
beforeLen := len(newTimeSeries)
newTimeSeries = newTimeSeries[len(newTimeSeries)-m.maxRecords:]
log.Warn().
Int("dropped", beforeLen-len(newTimeSeries)).
Int("kept", len(newTimeSeries)).
Msg("Metrics store exceeded maximum records, dropping oldest entries")
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 108-115
Issue Type: functional-high
Severity: high
Issue Description:
The 'dropped' count in the log message is computed after newTimeSeries has already been re-sliced, so len(m.timeSeries)-len(newTimeSeries) compares the original full slice against the already-truncated slice, producing an incorrect (inflated) dropped count.
Current Code:
if len(newTimeSeries) > m.maxRecords {
// Keep the most recent maxRecords entries
newTimeSeries = newTimeSeries[len(newTimeSeries)-m.maxRecords:]
log.Warn().
Int("dropped", len(m.timeSeries)-len(newTimeSeries)).
Int("kept", len(newTimeSeries)).
Msg("Metrics store exceeded maximum records, dropping oldest entries")
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| func NewInMemoryMetricsStore() *InMemoryMetricsStore { | ||
| store := &InMemoryMetricsStore{ | ||
| endpoints: make(map[string]int64), | ||
| models: make(map[string]int64), | ||
| backends: make(map[string]int64), | ||
| timeSeries: make([]RequestRecord, 0), | ||
| stopChan: make(chan struct{}), | ||
| maxRecords: 10000, // Limit to 10k records (~1-2MB of memory) | ||
| maxMapKeys: 1000, // Limit to 1000 unique keys per map (~50KB per map) | ||
| pruneEvery: 5 * time.Minute, // Prune every 5 minutes instead of every request | ||
| } | ||
|
|
||
| // Start background pruning goroutine | ||
| go store.pruneLoop() | ||
|
|
||
| return store | ||
| } |
There was a problem hiding this comment.
NewInMemoryMetricsStore starts a goroutine (pruneLoop) that holds a reference to the store, but there is no way for callers to know they must call Stop(); if the store is abandoned without Stop(), the goroutine leaks for the lifetime of the process.
Suggested fix
// NewInMemoryMetricsStore creates a new in-memory metrics store.
// Callers MUST call Stop() when the store is no longer needed to release the background goroutine.
func NewInMemoryMetricsStore() *InMemoryMetricsStore {
store := &InMemoryMetricsStore{
endpoints: make(map[string]int64),
models: make(map[string]int64),
backends: make(map[string]int64),
timeSeries: make([]RequestRecord, 0),
stopChan: make(chan struct{}),
maxRecords: 10000,
maxMapKeys: 1000,
pruneEvery: 5 * time.Minute,
}
go store.pruneLoop()
return store
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 60-76
Issue Type: robustness-high
Severity: high
Issue Description:
NewInMemoryMetricsStore starts a goroutine (pruneLoop) that holds a reference to the store, but there is no way for callers to know they must call Stop(); if the store is abandoned without Stop(), the goroutine leaks for the lifetime of the process.
Current Code:
func NewInMemoryMetricsStore() *InMemoryMetricsStore {
store := &InMemoryMetricsStore{
...
}
// Start background pruning goroutine
go store.pruneLoop()
return store
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| {{ if gt (len .ModelsConfig) 0 }} | ||
| this.fetchMetrics(); | ||
| // Auto-refresh every 30 seconds | ||
| setInterval(() => this.fetchMetrics(), 30000); |
There was a problem hiding this comment.
The setInterval at line 289 is never cleared; if the component is destroyed or re-initialized (e.g., via Alpine.js hot-reload or navigation), the interval leaks and continues firing, causing redundant fetches and potential errors on stale DOM references.
Suggested fix
this._metricsInterval = setInterval(() => this.fetchMetrics(), 30000);
// In a destroy/cleanup hook:
// clearInterval(this._metricsInterval);Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert javascript developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/index.html
Lines: 289-289
Issue Type: robustness-medium
Severity: medium
Issue Description:
The `setInterval` at line 289 is never cleared; if the component is destroyed or re-initialized (e.g., via Alpine.js hot-reload or navigation), the interval leaks and continues firing, causing redundant fetches and potential errors on stale DOM references.
Current Code:
setInterval(() => this.fetchMetrics(), 30000);
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow javascript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| <img {{ if and $cfg $cfg.Icon }} | ||
| src="{{$cfg.Icon}}" | ||
| {{ else }} | ||
| src="{{$noicon}}" | ||
| {{ end }} |
There was a problem hiding this comment.
The model icon URL from gallery config is rendered directly as an img src without validation, allowing the server-side data to cause the browser to make arbitrary outbound requests to attacker-controlled hosts if gallery config is compromised.
Suggested fix
<!-- Validate icon URLs server-side to only allow https:// scheme before passing to the template, or proxy images through a local endpoint -->
<!-- In the Go handler, sanitize $cfg.Icon to ensure it starts with https:// before rendering -->Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert html developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/settings.html
Lines: 166-170
Issue Type: security-medium
Severity: medium
Issue Description:
The model icon URL from gallery config is rendered directly as an img src without validation, allowing the server-side data to cause the browser to make arbitrary outbound requests to attacker-controlled hosts if gallery config is compromised.
Current Code:
<img {{ if and $cfg $cfg.Icon }}
src="{{$cfg.Icon}}"
{{ else }}
src="{{$noicon}}"
{{ end }}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow html best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| for i := 0; i < len(entries); i++ { | ||
| for j := i + 1; j < len(entries); j++ { | ||
| if entries[i].value < entries[j].value { | ||
| entries[i], entries[j] = entries[j], entries[i] | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
pruneMapIfNeeded uses a hand-rolled O(n²) bubble sort; for maps that can grow to maxMapKeys (1000 entries) this is unnecessarily slow and should use sort.Slice instead.
Also reported at: core/services/metrics.go L276–L282
Suggested fix
// Sort by value descending (keep highest counts)
sort.Slice(entries, func(i, j int) bool {
return entries[i].value > entries[j].value
})Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 143-149
Issue Type: performance-medium
Severity: medium
Issue Description:
pruneMapIfNeeded uses a hand-rolled O(n²) bubble sort; for maps that can grow to maxMapKeys (1000 entries) this is unnecessarily slow and should use sort.Slice instead.
_Also reported at: `core/services/metrics.go` L276–L282_
Current Code:
// Sort by value descending (keep highest counts)
for i := 0; i < len(entries); i++ {
for j := i + 1; j < len(entries); j++ {
if entries[i].value < entries[j].value {
entries[i], entries[j] = entries[j], entries[i]
}
}
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| func (m *InMemoryMetricsStore) GetRequestsOverTime(hours int) []TimeSeriesPoint { | ||
| m.mu.RLock() | ||
| defer m.mu.RUnlock() | ||
|
|
||
| cutoff := time.Now().Add(-time.Duration(hours) * time.Hour) |
There was a problem hiding this comment.
GetRequestsOverTime accepts an unbounded positive integer for 'hours'; a caller passing a very large value (e.g., math.MaxInt) causes time.Duration overflow in the cutoff calculation, returning incorrect results instead of an error or a capped value.
Suggested fix
func (m *InMemoryMetricsStore) GetRequestsOverTime(hours int) []TimeSeriesPoint {
const maxHours = 24 * 365 // cap at one year
if hours <= 0 {
hours = 24
} else if hours > maxHours {
hours = maxHours
}
m.mu.RLock()
defer m.mu.RUnlock()
cutoff := time.Now().Add(-time.Duration(hours) * time.Hour)Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 250-254
Issue Type: robustness-medium
Severity: medium
Issue Description:
GetRequestsOverTime accepts an unbounded positive integer for 'hours'; a caller passing a very large value (e.g., math.MaxInt) causes time.Duration overflow in the cutoff calculation, returning incorrect results instead of an error or a capped value.
Current Code:
func (m *InMemoryMetricsStore) GetRequestsOverTime(hours int) []TimeSeriesPoint {
m.mu.RLock()
defer m.mu.RUnlock()
cutoff := time.Now().Add(-time.Duration(hours) * time.Hour)
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| func (m *InMemoryMetricsStore) RecordRequest(endpoint, model, backend string, success bool, duration time.Duration) { | ||
| m.mu.Lock() | ||
| defer m.mu.Unlock() | ||
|
|
||
| // Record endpoint | ||
| if endpoint != "" { | ||
| m.endpoints[endpoint]++ | ||
| } | ||
|
|
||
| // Record model | ||
| if model != "" { | ||
| m.models[model]++ | ||
| } | ||
|
|
||
| // Record backend | ||
| if backend != "" { | ||
| m.backends[backend]++ | ||
| } | ||
|
|
||
| // Record success/failure | ||
| if success { | ||
| m.successCount++ | ||
| } else { | ||
| m.failureCount++ | ||
| } | ||
|
|
||
| // Add to time series | ||
| record := RequestRecord{ | ||
| Timestamp: time.Now(), | ||
| Endpoint: endpoint, | ||
| Model: model, | ||
| Backend: backend, | ||
| Success: success, | ||
| Duration: duration, | ||
| } | ||
| m.timeSeries = append(m.timeSeries, record) | ||
|
|
||
| // Note: Pruning is done periodically by pruneLoop() to avoid overhead on every request | ||
| } |
There was a problem hiding this comment.
Endpoint, model, and backend strings supplied by HTTP callers are stored as map keys without length or character validation; an attacker can craft arbitrarily long strings that consume memory before the periodic pruner runs.
Suggested fix
const maxKeyLen = 256
func truncateKey(s string) string {
if len(s) > maxKeyLen {
return s[:maxKeyLen]
}
return s
}
func (m *InMemoryMetricsStore) RecordRequest(endpoint, model, backend string, success bool, duration time.Duration) {
m.mu.Lock()
defer m.mu.Unlock()
if endpoint != "" {
m.endpoints[truncateKey(endpoint)]++
}
if model != "" {
m.models[truncateKey(model)]++
}
if backend != "" {
m.backends[truncateKey(backend)]++
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 173-211
Issue Type: security-medium
Severity: medium
Issue Description:
Endpoint, model, and backend strings supplied by HTTP callers are stored as map keys without length or character validation; an attacker can craft arbitrarily long strings that consume memory before the periodic pruner runs.
Current Code:
func (m *InMemoryMetricsStore) RecordRequest(endpoint, model, backend string, success bool, duration time.Duration) {
m.mu.Lock()
defer m.mu.Unlock()
if endpoint != "" {
m.endpoints[endpoint]++
}
if model != "" {
m.models[model]++
}
if backend != "" {
m.backends[backend]++
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
Security Scan Summary
No critical security issues detected Scan completed in 32.7sSecurity scan powered by Codity.ai |
Dependency vulnerability scanning
View vulnerability details (4 items)1. pip 24.0 CVE: GHSA-4xh5-x5gv-qwph
2. pip 24.0 CVE: GHSA-6vgw-5pg2-w6jp
3. pip 24.0 CVE: GHSA-58qw-9mgm-455v
4. pip 24.0 CVE: GHSA-jp4c-xjxw-mgf9
Powered by Codity.ai · Docs |
License Compliance Scan
Weak copyleft licenses found - verify compatibility Some packages have unknown licenses - manual review required Medium Risk Licenses - 4 packagesMPL-2.0 (4 packages):
Unknown Licenses - 51 packages
...and 31 more Powered by Codity.ai · Docs |
Code Quality Report — test-org-codity/LocalAI · PR #4Scanned: 2026-05-16 21:59 UTC | Score: 5/100 | Provider: github Executive Summary
Top Findings[CQ-LLM-002]
|
| File | Critical | High | Medium | Low | Total |
|---|---|---|---|---|---|
core/http/app.go |
0 | 0 | 1 | 1 | 2 |
core/http/endpoints/localai/settings.go |
0 | 1 | 1 | 0 | 2 |
core/http/middleware/metrics.go |
0 | 0 | 1 | 2 | 3 |
core/http/routes/ui_api.go |
0 | 0 | 0 | 2 | 2 |
core/http/views/index.html |
0 | 0 | 1 | 309 | 310 |
core/http/views/partials/navbar.html |
0 | 0 | 0 | 7 | 7 |
core/http/views/settings.html |
0 | 0 | 0 | 454 | 454 |
core/services/metrics.go |
0 | 0 | 0 | 2 | 2 |
Recommendations
- Resolve High severity issues, especially error handling gaps and performance bottlenecks.
- Run automated tests after applying fixes to verify no regressions.
|
@codity review |
Policy Check Failed✗ 3/3 policy checks failed: • Need 2 more approval(s) (0/2) — comment LGTM or approve via review To merge this PR:
|
PR SummaryWhat Changed
Key Changes by AreaMetrics System: New API: New UI/Dashboard: Complete UI/Settings: New Navigation: Added Settings link to desktop and mobile navbars. Files Changed
Review Focus Areas
ArchitectureDesign Decisions: In-memory metrics chosen for simplicity and low latency. Prometheus/OpenTelemetry remain for external monitoring. UI routes excluded from metrics to reduce noise. Auto-pruning (5 min interval) balances granularity with memory. Scalability & Extensibility: Metrics system is intentionally single-node. Out of scope: distributed metrics aggregation, persistent storage, or horizontal scaling. Token metrics endpoint stubbed but not wired. Risks:
Merge StatusNOT MERGEABLE — PR Score 3/100, below threshold (50)
|
|
|
||
| if !application.ApplicationConfig().DisableWebUI { | ||
|
|
||
| // Create metrics store for tracking usage (before API routes registration) | ||
| metricsStore := services.NewInMemoryMetricsStore() | ||
|
|
||
| // Add metrics middleware BEFORE API routes so it can intercept them | ||
| router.Use(middleware.MetricsMiddleware(metricsStore)) | ||
|
|
||
| // Register cleanup on shutdown | ||
| router.Hooks().OnShutdown(func() error { | ||
| metricsStore.Stop() | ||
| log.Info().Msg("Metrics store stopped") | ||
| return nil | ||
| }) | ||
|
|
||
| // Create opcache for tracking UI operations | ||
| opcache := services.NewOpCache(application.GalleryService()) | ||
| routes.RegisterUIAPIRoutes(router, application.ModelConfigLoader(), application.ApplicationConfig(), application.GalleryService(), opcache) | ||
| routes.RegisterUIAPIRoutes(router, application.ModelConfigLoader(), application.ApplicationConfig(), application.GalleryService(), opcache, metricsStore) |
There was a problem hiding this comment.
The metrics middleware is registered AFTER the API routes (ElevenLabs, LocalAI, OpenAI, JINA), so it will never intercept requests to those routes; move router.Use(middleware.MetricsMiddleware(metricsStore)) to before the route registrations.
Suggested fix
// Create metrics store for tracking usage (before API routes registration)
var metricsStore services.MetricsStore
if !application.ApplicationConfig().DisableWebUI {
metricsStore = services.NewInMemoryMetricsStore()
// Add metrics middleware BEFORE API routes so it can intercept them
router.Use(middleware.MetricsMiddleware(metricsStore))
// Register cleanup on shutdown
router.Hooks().OnShutdown(func() error {
metricsStore.Stop()
log.Info().Msg("Metrics store stopped")
return nil
})
}
routes.RegisterElevenLabsRoutes(router, ...)
routes.RegisterLocalAIRoutes(router, ...)
routes.RegisterOpenAIRoutes(router, ...)
if !application.ApplicationConfig().DisableWebUI {
opcache := services.NewOpCache(application.GalleryService())
routes.RegisterUIAPIRoutes(router, application.ModelConfigLoader(), application.ApplicationConfig(), application.GalleryService(), opcache, metricsStore)
routes.RegisterUIRoutes(router, application.ModelConfigLoader(), application.ModelLoader(), application.ApplicationConfig(), application.GalleryService())
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/app.go
Lines: 207-225
Issue Type: functional-high
Severity: high
Issue Description:
The metrics middleware is registered AFTER the API routes (ElevenLabs, LocalAI, OpenAI, JINA), so it will never intercept requests to those routes; move `router.Use(middleware.MetricsMiddleware(metricsStore))` to before the route registrations.
Current Code:
routes.RegisterElevenLabsRoutes(router, ...)
routes.RegisterLocalAIRoutes(router, ...)
routes.RegisterOpenAIRoutes(router, ...)
if !application.ApplicationConfig().DisableWebUI {
metricsStore := services.NewInMemoryMetricsStore()
router.Use(middleware.MetricsMiddleware(metricsStore))
...
routes.RegisterUIAPIRoutes(router, ..., metricsStore)
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| "Models": modelsWithoutConfig, | ||
| "ModelsConfig": modelConfigs, | ||
| "GalleryConfig": galleryConfigs, | ||
| "ApplicationConfig": appConfig, |
There was a problem hiding this comment.
The full appConfig (ApplicationConfig) is passed directly into the template map and rendered to the UI; this may expose sensitive configuration fields (API keys, secrets, internal paths) to the browser.
Suggested fix
// Only expose the specific fields the template actually needs, not the entire config struct.
// e.g. "DebugMode": appConfig.Debug,Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/endpoints/localai/settings.go
Lines: 51-51
Issue Type: security-high
Severity: high
Issue Description:
The full `appConfig` (ApplicationConfig) is passed directly into the template map and rendered to the UI; this may expose sensitive configuration fields (API keys, secrets, internal paths) to the browser.
Current Code:
"ApplicationConfig": appConfig,
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| app.Post("/api/metrics/reset", func(c *fiber.Ctx) error { | ||
| metricsStore.Reset() | ||
| return c.JSON(fiber.Map{ | ||
| "success": true, | ||
| "message": "Metrics reset successfully", | ||
| }) | ||
| }) |
There was a problem hiding this comment.
The /api/metrics/reset endpoint is unauthenticated and unguarded, allowing any user or unauthenticated request to wipe all metrics data; it should require admin authorization or be removed from production.
Suggested fix
// Reset metrics - admin only
app.Post("/api/metrics/reset", func(c *fiber.Ctx) error {
// TODO: add admin/role check before allowing reset
metricsStore.Reset()
return c.JSON(fiber.Map{
"success": true,
"message": "Metrics reset successfully",
})
})Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/routes/ui_api.go
Lines: 811-817
Issue Type: security-high
Severity: high
Issue Description:
The `/api/metrics/reset` endpoint is unauthenticated and unguarded, allowing any user or unauthenticated request to wipe all metrics data; it should require admin authorization or be removed from production.
Current Code:
// Reset metrics (optional - for testing/admin purposes)
app.Post("/api/metrics/reset", func(c *fiber.Ctx) error {
metricsStore.Reset()
return c.JSON(fiber.Map{
"success": true,
"message": "Metrics reset successfully",
})
})
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
|
|
||
| <body class="bg-[#101827] text-[#E5E7EB]"> | ||
| <div class="flex flex-col min-h-screen" x-data="indexDashboard()"> | ||
| <div class="flex flex-col min-h-screen" x-data="dashboardMetrics()"> |
There was a problem hiding this comment.
The x-data attribute references dashboardMetrics() (line 6) but the old code used indexDashboard(), and the charts/stats cards rely on Alpine.js properties (totalRequests, successRate, hasEndpointData, hasModelData, hasTimelineData) that must be defined in this new component; if dashboardMetrics() is not defined or missing these properties, all dynamic stat cards and charts will silently show stale/zero values or throw Alpine errors.
Also reported at: core/http/views/index.html L6
Suggested fix
<!-- Ensure dashboardMetrics() is defined in a <script> block and exposes:
totalRequests, successRate, hasEndpointData, hasModelData, hasTimelineData
and initialises Chart.js instances for endpointChart, modelChart, timelineChart -->
<div class="flex flex-col min-h-screen" x-data="dashboardMetrics()">Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert html developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/index.html
Lines: 6-6
Issue Type: functional-high
Severity: high
Issue Description:
The `x-data` attribute references `dashboardMetrics()` (line 6) but the old code used `indexDashboard()`, and the charts/stats cards rely on Alpine.js properties (`totalRequests`, `successRate`, `hasEndpointData`, `hasModelData`, `hasTimelineData`) that must be defined in this new component; if `dashboardMetrics()` is not defined or missing these properties, all dynamic stat cards and charts will silently show stale/zero values or throw Alpine errors.
_Also reported at: `core/http/views/index.html` L6_
Current Code:
<div class="flex flex-col min-h-screen" x-data="dashboardMetrics()">
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow html best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| func (m *InMemoryMetricsStore) Stop() { | ||
| close(m.stopChan) | ||
| } |
There was a problem hiding this comment.
Calling Stop() more than once panics because close on an already-closed channel causes a runtime panic; guard with a sync.Once to make it safe.
Suggested fix
func (m *InMemoryMetricsStore) Stop() {
m.mu.Lock()
defer m.mu.Unlock()
select {
case <-m.stopChan:
// already closed
default:
close(m.stopChan)
}
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 168-170
Issue Type: robustness-high
Severity: high
Issue Description:
Calling `Stop()` more than once panics because `close` on an already-closed channel causes a runtime panic; guard with a `sync.Once` to make it safe.
Current Code:
func (m *InMemoryMetricsStore) Stop() {
close(m.stopChan)
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| const data = await response.json(); | ||
|
|
||
| if (response.ok && data.success) { | ||
| this.addNotification(`Backend "${backendName}" deleted successfully!`, 'success'); | ||
| // Reload page after short delay | ||
| setTimeout(() => { | ||
| window.location.reload(); | ||
| }, 1500); | ||
| } else { | ||
| this.addNotification(`Failed to delete backend: ${data.error || 'Unknown error'}`, 'error'); | ||
| } |
There was a problem hiding this comment.
The deleteBackend function calls /api/backends/system/delete/ but response.json() is called unconditionally before checking response.ok; if the server returns a non-JSON error body (e.g., 404 HTML), this will throw and the catch block will surface a confusing parse error instead of the real HTTP error.
Also reported at: core/http/views/settings.html L562–L588
Suggested fix
let data = {};
try { data = await response.json(); } catch (_) {}
if (response.ok && data.success) {Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert javascript developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/settings.html
Lines: 476-486
Issue Type: functional-medium
Severity: medium
Issue Description:
The `deleteBackend` function calls `/api/backends/system/delete/` but `response.json()` is called unconditionally before checking `response.ok`; if the server returns a non-JSON error body (e.g., 404 HTML), this will throw and the catch block will surface a confusing parse error instead of the real HTTP error.
_Also reported at: `core/http/views/settings.html` L562–L588_
Current Code:
const data = await response.json();
if (response.ok && data.success) {
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow javascript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| const icon = button.querySelector('i'); | ||
|
|
||
| // Show loading state | ||
| button.disabled = true; | ||
| button.querySelector('span').textContent = 'Updating...'; | ||
| icon.classList.add('fa-spin'); |
There was a problem hiding this comment.
The icon variable is captured before the button is disabled; if the button has multiple <i> elements (it has two: fa-sync-alt and fa-refresh), querySelector('i') only returns the first one, so the second icon never gets the spin class and the visual feedback is incomplete.
Suggested fix
const icons = button.querySelectorAll('i');
const icon = icons[0]; // primary icon
// Show loading state
button.disabled = true;
button.querySelector('span').textContent = 'Updating...';
icon.classList.add('fa-spin');Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert javascript developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/settings.html
Lines: 548-553
Issue Type: robustness-medium
Severity: medium
Issue Description:
The `icon` variable is captured before the button is disabled; if the button has multiple `<i>` elements (it has two: `fa-sync-alt` and `fa-refresh`), `querySelector('i')` only returns the first one, so the second icon never gets the spin class and the visual feedback is incomplete.
Current Code:
const icon = button.querySelector('i');
// Show loading state
button.disabled = true;
button.querySelector('span').textContent = 'Updating...';
icon.classList.add('fa-spin');
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow javascript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| {{ if and $backendCfg (or (ne $backendCfg.MCP.Servers "") (ne $backendCfg.MCP.Stdio "")) }} | ||
| <span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold bg-[#8B5CF6]/20 text-[#8B5CF6] border border-[#8B5CF6]/30"> | ||
| <i class="fas fa-plug mr-1"></i>MCP | ||
| </span> | ||
| {{ end }} |
There was a problem hiding this comment.
The MCP badge condition uses ne $backendCfg.MCP.Servers "" and ne $backendCfg.MCP.Stdio "" but if these fields are slices or structs rather than plain strings, the Go template ne comparison against "" will always evaluate to true (non-empty interface), showing the MCP badge incorrectly for every model.
| {{ if and $backendCfg (or (ne $backendCfg.MCP.Servers "") (ne $backendCfg.MCP.Stdio "")) }} | |
| <span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold bg-[#8B5CF6]/20 text-[#8B5CF6] border border-[#8B5CF6]/30"> | |
| <i class="fas fa-plug mr-1"></i>MCP | |
| </span> | |
| {{ end }} | |
| {{ if and $backendCfg (or $backendCfg.MCP.Servers $backendCfg.MCP.Stdio) }} |
Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert html developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/settings.html
Lines: 195-199
Issue Type: functional-medium
Severity: medium
Issue Description:
The MCP badge condition uses `ne $backendCfg.MCP.Servers ""` and `ne $backendCfg.MCP.Stdio ""` but if these fields are slices or structs rather than plain strings, the Go template `ne` comparison against `""` will always evaluate to true (non-empty interface), showing the MCP badge incorrectly for every model.
Current Code:
{{ if and $backendCfg (or (ne $backendCfg.MCP.Servers "") (ne $backendCfg.MCP.Stdio "")) }}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow html best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| if len(newTimeSeries) > m.maxRecords { | ||
| // Keep the most recent maxRecords entries | ||
| newTimeSeries = newTimeSeries[len(newTimeSeries)-m.maxRecords:] | ||
| log.Warn(). | ||
| Int("dropped", len(m.timeSeries)-len(newTimeSeries)). | ||
| Int("kept", len(newTimeSeries)). | ||
| Msg("Metrics store exceeded maximum records, dropping oldest entries") | ||
| } |
There was a problem hiding this comment.
The dropped count in the log warning is computed after newTimeSeries has already been re-sliced to maxRecords, so len(m.timeSeries) - len(newTimeSeries) equals original_count - maxRecords and incorrectly includes records already removed by the 24-hour cutoff filter; capture the pre-slice length before line 110 to log the accurate count.
Suggested fix
if len(newTimeSeries) > m.maxRecords {
// Keep the most recent maxRecords entries
preSliceLen := len(newTimeSeries)
newTimeSeries = newTimeSeries[len(newTimeSeries)-m.maxRecords:]
log.Warn().
Int("dropped", preSliceLen-len(newTimeSeries)).
Int("kept", len(newTimeSeries)).
Msg("Metrics store exceeded maximum records, dropping oldest entries")
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 108-115
Issue Type: functional-medium
Severity: medium
Issue Description:
The `dropped` count in the log warning is computed after `newTimeSeries` has already been re-sliced to `maxRecords`, so `len(m.timeSeries) - len(newTimeSeries)` equals `original_count - maxRecords` and incorrectly includes records already removed by the 24-hour cutoff filter; capture the pre-slice length before line 110 to log the accurate count.
Current Code:
if len(newTimeSeries) > m.maxRecords {
// Keep the most recent maxRecords entries
newTimeSeries = newTimeSeries[len(newTimeSeries)-m.maxRecords:]
log.Warn().
Int("dropped", len(m.timeSeries)-len(newTimeSeries)).
Int("kept", len(newTimeSeries)).
Msg("Metrics store exceeded maximum records, dropping oldest entries")
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| for i := 0; i < len(entries); i++ { | ||
| for j := i + 1; j < len(entries); j++ { | ||
| if entries[i].value < entries[j].value { | ||
| entries[i], entries[j] = entries[j], entries[i] | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Both pruneMapIfNeeded (lines 143-149) and GetRequestsOverTime (lines 276-282) use O(n²) bubble sort; replace with sort.Slice to avoid quadratic time on large inputs.
Also reported at: core/services/metrics.go L276–L282
Suggested fix
// Sort by value descending (keep highest counts)
sort.Slice(entries, func(i, j int) bool {
return entries[i].value > entries[j].value
})Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 143-149
Issue Type: performance-medium
Severity: medium
Issue Description:
Both `pruneMapIfNeeded` (lines 143-149) and `GetRequestsOverTime` (lines 276-282) use O(n²) bubble sort; replace with `sort.Slice` to avoid quadratic time on large inputs.
_Also reported at: `core/services/metrics.go` L276–L282_
Current Code:
// Sort by value descending (keep highest counts)
for i := 0; i < len(entries); i++ {
for j := i + 1; j < len(entries); j++ {
if entries[i].value < entries[j].value {
entries[i], entries[j] = entries[j], entries[i]
}
}
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
Security Scan Summary
No critical security issues detected Scan completed in 34.1sSecurity scan powered by Codity.ai |
Dependency vulnerability scanning
View vulnerability details (4 items)1. pip 24.0 CVE: GHSA-4xh5-x5gv-qwph
2. pip 24.0 CVE: GHSA-6vgw-5pg2-w6jp
3. pip 24.0 CVE: GHSA-58qw-9mgm-455v
4. pip 24.0 CVE: GHSA-jp4c-xjxw-mgf9
Powered by Codity.ai · Docs |
License Compliance Scan
Weak copyleft licenses found - verify compatibility Some packages have unknown licenses - manual review required Medium Risk Licenses - 4 packagesMPL-2.0 (4 packages):
Unknown Licenses - 46 packages
...and 26 more Powered by Codity.ai · Docs |
Code Quality Report — test-org-codity/LocalAI · PR #4Scanned: 2026-05-17 16:21 UTC | Score: 5/100 | Provider: github Executive Summary
Top Findings[CQ-LLM-002]
|
| File | Critical | High | Medium | Low | Total |
|---|---|---|---|---|---|
core/http/app.go |
0 | 0 | 1 | 1 | 2 |
core/http/endpoints/localai/settings.go |
0 | 1 | 0 | 1 | 2 |
core/http/middleware/metrics.go |
0 | 0 | 2 | 1 | 3 |
core/http/routes/ui_api.go |
0 | 0 | 0 | 2 | 2 |
core/http/views/index.html |
0 | 0 | 1 | 309 | 310 |
core/http/views/partials/navbar.html |
0 | 0 | 0 | 7 | 7 |
core/http/views/settings.html |
0 | 0 | 0 | 454 | 454 |
core/services/metrics.go |
0 | 0 | 0 | 2 | 2 |
Recommendations
- Resolve High severity issues, especially error handling gaps and performance bottlenecks.
- Run automated tests after applying fixes to verify no regressions.
|
@codity review |
Policy Check Failed✗ 3/3 policy checks failed: • Need 2 more approval(s) (0/2) — comment LGTM or approve via review To merge this PR:
|
PR SummaryWhat Changed
Key Changes by AreaMetrics Infrastructure: In-memory store with 24-hour data retention and 5-minute background pruning to prevent memory bloat. Thread-safe with API: New endpoints for metrics summary, timeseries data, and admin reset. Settings endpoint aggregates model configs, gallery configs, and operation statuses. UI/UX: Chart.js integration with auto-refreshing dashboards. Consistent gradient styling across all pages. Toast notification system for async operation feedback. Files Changed
Review Focus Areas
ArchitectureDesign Decisions: In-memory storage chosen for simplicity with explicit pruning instead of persistence. Interface-based design ( Scalability & Extensibility: Current implementation is single-node only. Out of scope: distributed metrics aggregation, persistent storage, or external metrics export (Prometheus/OpenTelemetry scaffolding exists but not wired). Risks: Memory usage can spike between pruning intervals under heavy load. Intentional: data loss on restart (acceptable for usage stats). Unintentional: pruning may drop useful historical data if request patterns are bursty. Merge StatusNOT MERGEABLE — PR Score 3/100, below threshold (50)
|
| newTimeSeries = newTimeSeries[len(newTimeSeries)-m.maxRecords:] | ||
| log.Warn(). | ||
| Int("dropped", len(m.timeSeries)-len(newTimeSeries)). | ||
| Int("kept", len(newTimeSeries)). | ||
| Msg("Metrics store exceeded maximum records, dropping oldest entries") |
There was a problem hiding this comment.
The dropped count in the log message is computed as len(m.timeSeries)-len(newTimeSeries) AFTER newTimeSeries has already been sliced down to maxRecords, so len(newTimeSeries) is always maxRecords and the logged drop count is wrong; capture the pre-slice length before reassigning.
Suggested fix
originalLen := len(newTimeSeries)
newTimeSeries = newTimeSeries[len(newTimeSeries)-m.maxRecords:]
log.Warn().
Int("dropped", originalLen-len(newTimeSeries)).
Int("kept", len(newTimeSeries)).
Msg("Metrics store exceeded maximum records, dropping oldest entries")Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 110-114
Issue Type: functional-critical
Severity: critical
Issue Description:
The `dropped` count in the log message is computed as `len(m.timeSeries)-len(newTimeSeries)` AFTER `newTimeSeries` has already been sliced down to `maxRecords`, so `len(newTimeSeries)` is always `maxRecords` and the logged drop count is wrong; capture the pre-slice length before reassigning.
Current Code:
newTimeSeries = newTimeSeries[len(newTimeSeries)-m.maxRecords:]
log.Warn().
Int("dropped", len(m.timeSeries)-len(newTimeSeries)).
Int("kept", len(newTimeSeries)).
Msg("Metrics store exceeded maximum records, dropping oldest entries")
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
|
|
||
| if !application.ApplicationConfig().DisableWebUI { | ||
|
|
||
| // Create metrics store for tracking usage (before API routes registration) | ||
| metricsStore := services.NewInMemoryMetricsStore() | ||
|
|
||
| // Add metrics middleware BEFORE API routes so it can intercept them | ||
| router.Use(middleware.MetricsMiddleware(metricsStore)) | ||
|
|
||
| // Register cleanup on shutdown | ||
| router.Hooks().OnShutdown(func() error { | ||
| metricsStore.Stop() | ||
| log.Info().Msg("Metrics store stopped") | ||
| return nil | ||
| }) | ||
|
|
||
| // Create opcache for tracking UI operations | ||
| opcache := services.NewOpCache(application.GalleryService()) | ||
| routes.RegisterUIAPIRoutes(router, application.ModelConfigLoader(), application.ApplicationConfig(), application.GalleryService(), opcache) | ||
| routes.RegisterUIAPIRoutes(router, application.ModelConfigLoader(), application.ApplicationConfig(), application.GalleryService(), opcache, metricsStore) |
There was a problem hiding this comment.
The MetricsMiddleware is registered after RegisterOpenAIRoutes, RegisterLocalAIRoutes, and RegisterElevenLabsRoutes, so it will never intercept those API requests; the comment on line 213 states it should be added BEFORE API routes.
Also reported at: core/http/app.go L208–L221
Suggested fix
if !application.ApplicationConfig().DisableWebUI {
// Create metrics store for tracking usage (before API routes registration)
metricsStore := services.NewInMemoryMetricsStore()
// Add metrics middleware BEFORE API routes so it can intercept them
router.Use(middleware.MetricsMiddleware(metricsStore))
// Register cleanup on shutdown
router.Hooks().OnShutdown(func() error {
metricsStore.Stop()
log.Info().Msg("Metrics store stopped")
return nil
})
}
routes.RegisterElevenLabsRoutes(...)
routes.RegisterLocalAIRoutes(...)
routes.RegisterOpenAIRoutes(...)
if !application.ApplicationConfig().DisableWebUI {
opcache := services.NewOpCache(application.GalleryService())
routes.RegisterUIAPIRoutes(router, application.ModelConfigLoader(), application.ApplicationConfig(), application.GalleryService(), opcache, metricsStore)
routes.RegisterUIRoutes(...)
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/app.go
Lines: 207-225
Issue Type: functional-high
Severity: high
Issue Description:
The MetricsMiddleware is registered after RegisterOpenAIRoutes, RegisterLocalAIRoutes, and RegisterElevenLabsRoutes, so it will never intercept those API requests; the comment on line 213 states it should be added BEFORE API routes.
_Also reported at: `core/http/app.go` L208–L221_
Current Code:
routes.RegisterOpenAIRoutes(router, requestExtractor, application)
if !application.ApplicationConfig().DisableWebUI {
// Create metrics store for tracking usage (before API routes registration)
metricsStore := services.NewInMemoryMetricsStore()
// Add metrics middleware BEFORE API routes so it can intercept them
router.Use(middleware.MetricsMiddleware(metricsStore))
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| "Models": modelsWithoutConfig, | ||
| "ModelsConfig": modelConfigs, | ||
| "GalleryConfig": galleryConfigs, | ||
| "ApplicationConfig": appConfig, |
There was a problem hiding this comment.
appConfig (the full application configuration) is passed directly into the template map and rendered to the UI, potentially exposing sensitive internal configuration fields (API keys, secrets, paths) to end users.
Suggested fix
// Only expose the specific fields needed by the template rather than the full appConfig struct
"ApplicationConfig": appConfig, // TODO: replace with a safe DTO containing only UI-relevant fieldsPrompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/endpoints/localai/settings.go
Lines: 51-51
Issue Type: security-high
Severity: high
Issue Description:
`appConfig` (the full application configuration) is passed directly into the template map and rendered to the UI, potentially exposing sensitive internal configuration fields (API keys, secrets, paths) to end users.
Current Code:
"ApplicationConfig": appConfig,
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| app.Post("/api/metrics/reset", func(c *fiber.Ctx) error { | ||
| metricsStore.Reset() | ||
| return c.JSON(fiber.Map{ | ||
| "success": true, | ||
| "message": "Metrics reset successfully", | ||
| }) | ||
| }) |
There was a problem hiding this comment.
The /api/metrics/reset endpoint is unauthenticated and unprotected, allowing any user or anonymous caller to wipe all metrics data; it should require admin authorization or be removed from production.
Suggested fix
// Reset metrics - admin only
app.Post("/api/metrics/reset", adminAuthMiddleware, func(c *fiber.Ctx) error {
metricsStore.Reset()
return c.JSON(fiber.Map{
"success": true,
"message": "Metrics reset successfully",
})
})Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/routes/ui_api.go
Lines: 811-817
Issue Type: security-high
Severity: high
Issue Description:
The `/api/metrics/reset` endpoint is unauthenticated and unprotected, allowing any user or anonymous caller to wipe all metrics data; it should require admin authorization or be removed from production.
Current Code:
// Reset metrics (optional - for testing/admin purposes)
app.Post("/api/metrics/reset", func(c *fiber.Ctx) error {
metricsStore.Reset()
return c.JSON(fiber.Map{
"success": true,
"message": "Metrics reset successfully",
})
})
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
|
|
||
| <body class="bg-[#101827] text-[#E5E7EB]"> | ||
| <div class="flex flex-col min-h-screen" x-data="indexDashboard()"> | ||
| <div class="flex flex-col min-h-screen" x-data="dashboardMetrics()"> |
There was a problem hiding this comment.
The notification system (x-for="notification in notifications", dismissNotification, etc.) was removed along with the indexDashboard() Alpine.js component, but the root element now references dashboardMetrics() instead; if dashboardMetrics does not expose notifications/dismissNotification, any code elsewhere that still triggers notifications will silently fail or throw a JS error.
Suggested fix
<!-- Ensure dashboardMetrics() in the companion JS file exposes all properties
previously provided by indexDashboard() that are still needed (e.g. notifications),
or restore a separate notification container driven by a shared Alpine store. -->Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert html developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/index.html
Lines: 6-6
Issue Type: functional-high
Severity: high
Issue Description:
The notification system (`x-for="notification in notifications"`, `dismissNotification`, etc.) was removed along with the `indexDashboard()` Alpine.js component, but the root element now references `dashboardMetrics()` instead; if `dashboardMetrics` does not expose `notifications`/`dismissNotification`, any code elsewhere that still triggers notifications will silently fail or throw a JS error.
Current Code:
<div class="flex flex-col min-h-screen" x-data="dashboardMetrics()">
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow html best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| const data = await response.json(); | ||
|
|
||
| if (response.ok && data.success) { | ||
| this.addNotification(`Backend "${backendName}" deleted successfully!`, 'success'); | ||
| // Reload page after short delay | ||
| setTimeout(() => { | ||
| window.location.reload(); | ||
| }, 1500); | ||
| } else { | ||
| this.addNotification(`Failed to delete backend: ${data.error || 'Unknown error'}`, 'error'); | ||
| } |
There was a problem hiding this comment.
The deleteBackend function calls /api/backends/system/delete/ but response.json() is called unconditionally before checking response.ok; if the server returns a non-JSON error body (e.g., 404 HTML), this will throw and the catch block will surface a confusing parse error instead of the real HTTP error.
Suggested fix
let data = {};
try { data = await response.json(); } catch (_) {}
if (response.ok && data.success) {Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert javascript developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/settings.html
Lines: 476-486
Issue Type: functional-medium
Severity: medium
Issue Description:
The `deleteBackend` function calls `/api/backends/system/delete/` but `response.json()` is called unconditionally before checking `response.ok`; if the server returns a non-JSON error body (e.g., 404 HTML), this will throw and the catch block will surface a confusing parse error instead of the real HTTP error.
Current Code:
const data = await response.json();
if (response.ok && data.success) {
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow javascript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| {{template "views/partials/head" .}} | ||
|
|
||
| <body class="bg-[#101827] text-[#E5E7EB]"> | ||
| <div class="flex flex-col min-h-screen" x-data="indexDashboard()"> |
There was a problem hiding this comment.
The x-data attribute on the root <div> uses indexDashboard() (line 6), but this is a settings page; the Alpine component is named indexDashboard which is semantically misleading and could conflict if index.html also defines a global indexDashboard function in the same page scope.
| <div class="flex flex-col min-h-screen" x-data="indexDashboard()"> | |
| <div class="flex flex-col min-h-screen" x-data="settingsDashboard()"> |
Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert html developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/settings.html
Lines: 6-6
Issue Type: functional-medium
Severity: medium
Issue Description:
The `x-data` attribute on the root `<div>` uses `indexDashboard()` (line 6), but this is a settings page; the Alpine component is named `indexDashboard` which is semantically misleading and could conflict if `index.html` also defines a global `indexDashboard` function in the same page scope.
Current Code:
<div class="flex flex-col min-h-screen" x-data="indexDashboard()">
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow html best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| for i := 0; i < len(entries); i++ { | ||
| for j := i + 1; j < len(entries); j++ { | ||
| if entries[i].value < entries[j].value { | ||
| entries[i], entries[j] = entries[j], entries[i] | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
The bubble-sort in pruneMapIfNeeded is O(n²) over up to maxMapKeys (1000) entries; replace with sort.Slice for O(n log n) performance.
Also reported at: core/services/metrics.go L276–L282
Suggested fix
// Sort by value descending (keep highest counts)
sort.Slice(entries, func(i, j int) bool {
return entries[i].value > entries[j].value
})Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 143-149
Issue Type: performance-medium
Severity: medium
Issue Description:
The bubble-sort in `pruneMapIfNeeded` is O(n²) over up to `maxMapKeys` (1000) entries; replace with `sort.Slice` for O(n log n) performance.
_Also reported at: `core/services/metrics.go` L276–L282_
Current Code:
// Sort by value descending (keep highest counts)
for i := 0; i < len(entries); i++ {
for j := i + 1; j < len(entries); j++ {
if entries[i].value < entries[j].value {
entries[i], entries[j] = entries[j], entries[i]
}
}
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
|
|
||
| // InMemoryMetricsStore implements MetricsStore with in-memory storage |
There was a problem hiding this comment.
LocalAIMetricsAPIMiddleware calls cfg.metricsService.ObserveAPICall unconditionally even when metrics is nil, which will panic at runtime if the service is not initialised; add a nil guard before calling.
Suggested fix
elapsed := float64(time.Since(start)) / float64(time.Second)
if cfg.metricsService != nil {
cfg.metricsService.ObserveAPICall(method, path, elapsed)
}
return errPrompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 43-44
Issue Type: robustness-medium
Severity: medium
Issue Description:
`LocalAIMetricsAPIMiddleware` calls `cfg.metricsService.ObserveAPICall` unconditionally even when `metrics` is nil, which will panic at runtime if the service is not initialised; add a nil guard before calling.
Current Code:
elapsed := float64(time.Since(start)) / float64(time.Second)
cfg.metricsService.ObserveAPICall(method, path, elapsed)
return err
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| Duration time.Duration | ||
| } | ||
|
|
||
| // InMemoryMetricsStore implements MetricsStore with in-memory storage |
There was a problem hiding this comment.
In get_token_metrics.go, log.Err(err) does not send the log entry (missing .Msg() or .Send()), so the error is silently swallowed; replace with log.Err(err).Msg("failed to load model config") or use log.Error().Err(err).Msg(...).
| // InMemoryMetricsStore implements MetricsStore with in-memory storage | |
| log.Error().Err(err).Msg("failed to load model config") |
Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 44-44
Issue Type: robustness-medium
Severity: medium
Issue Description:
In `get_token_metrics.go`, `log.Err(err)` does not send the log entry (missing `.Msg()` or `.Send()`), so the error is silently swallowed; replace with `log.Err(err).Msg("failed to load model config")` or use `log.Error().Err(err).Msg(...)`.
Current Code:
log.Err(err)
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
Security Scan Summary
No critical security issues detected Scan completed in 29.1sSecurity scan powered by Codity.ai |
Dependency vulnerability scanning
View vulnerability details (4 items)1. pip 24.0 CVE: GHSA-4xh5-x5gv-qwph
2. pip 24.0 CVE: GHSA-6vgw-5pg2-w6jp
3. pip 24.0 CVE: GHSA-58qw-9mgm-455v
4. pip 24.0 CVE: GHSA-jp4c-xjxw-mgf9
Powered by Codity.ai · Docs |
License Compliance Scan
Weak copyleft licenses found - verify compatibility Some packages have unknown licenses - manual review required Medium Risk Licenses - 4 packagesMPL-2.0 (4 packages):
Unknown Licenses - 58 packages
...and 38 more Powered by Codity.ai · Docs |
Code Quality Report — test-org-codity/LocalAI · PR #4Scanned: 2026-05-17 17:02 UTC | Score: 5/100 | Provider: github Executive Summary
Top Findings[CQ-LLM-002]
|
| File | Critical | High | Medium | Low | Total |
|---|---|---|---|---|---|
core/http/app.go |
0 | 0 | 1 | 1 | 2 |
core/http/endpoints/localai/settings.go |
0 | 1 | 1 | 0 | 2 |
core/http/middleware/metrics.go |
0 | 0 | 1 | 2 | 3 |
core/http/routes/ui_api.go |
0 | 0 | 0 | 2 | 2 |
core/http/views/index.html |
0 | 0 | 1 | 309 | 310 |
core/http/views/partials/navbar.html |
0 | 0 | 0 | 7 | 7 |
core/http/views/settings.html |
0 | 0 | 0 | 454 | 454 |
core/services/metrics.go |
0 | 0 | 0 | 2 | 2 |
Recommendations
- Resolve High severity issues, especially error handling gaps and performance bottlenecks.
- Run automated tests after applying fixes to verify no regressions.
|
@codity review |
Policy Check Failed✗ 3/3 policy checks failed: • Need 2 more approval(s) (0/2) — comment LGTM or approve via review To merge this PR:
|
PR SummaryWhat Changed
Key Changes by AreaMetrics System
Web UI
API
Files Changed
Review Focus Areas
ArchitectureDesign Decisions
Scalability & Extensibility
Risks
Merge StatusNOT MERGEABLE — PR Score 3/100, below threshold (50)
|
Security Scan Summary
Consider addressing security findings before merging Scan completed in 64.5s View vulnerability details (2 items)1. SEC-001 (CWE-79) HIGH File:
Fix: Do not embed untrusted data directly into inline JavaScript. Pass the model name via a data attribute and retrieve it in the handler, or ensure it is properly JavaScript-escaped (e.g. by using 2. SEC-001 (CWE-79) HIGH File:
Fix: Avoid embedding untrusted values in inline JS. Move the name into a Security scan powered by Codity.ai |
Dependency vulnerability scanning
View vulnerability details (4 items)1. pip 24.0 CVE: GHSA-4xh5-x5gv-qwph
2. pip 24.0 CVE: GHSA-6vgw-5pg2-w6jp
3. pip 24.0 CVE: GHSA-58qw-9mgm-455v
4. pip 24.0 CVE: GHSA-jp4c-xjxw-mgf9
Powered by Codity.ai · Docs |
License Compliance Scan
Weak copyleft licenses found - verify compatibility Some packages have unknown licenses - manual review required Medium Risk Licenses - 4 packagesMPL-2.0 (4 packages):
Unknown Licenses - 24 packages
...and 4 more Powered by Codity.ai · Docs |
Code Quality Report — test-org-codity/LocalAI · PR #4Scanned: 2026-05-19 15:29 UTC | Score: 5/100 | Provider: github Executive Summary
Top Findings[CQ-LLM-002]
|
| File | Critical | High | Medium | Low | Total |
|---|---|---|---|---|---|
core/http/app.go |
0 | 0 | 1 | 1 | 2 |
core/http/endpoints/localai/settings.go |
0 | 1 | 1 | 1 | 3 |
core/http/middleware/metrics.go |
0 | 0 | 3 | 1 | 4 |
core/http/routes/ui_api.go |
0 | 0 | 0 | 2 | 2 |
core/http/views/index.html |
0 | 0 | 1 | 309 | 310 |
core/http/views/partials/navbar.html |
0 | 0 | 0 | 7 | 7 |
core/http/views/settings.html |
0 | 0 | 0 | 454 | 454 |
core/services/metrics.go |
0 | 0 | 0 | 2 | 2 |
Recommendations
- Resolve High severity issues, especially error handling gaps and performance bottlenecks.
- Run automated tests after applying fixes to verify no regressions.
|
@codity review |
Policy Check Failed✗ 3/3 policy checks failed: • Need 2 more approval(s) (0/2) — comment LGTM or approve via review To merge this PR:
|
PR SummaryWhat Changed
Key Changes by AreaMetrics Infrastructure: New Dashboard UI: Split index view into welcome flow (no models) vs. metrics dashboard (with models). Added Chart.js visualizations with 30-second auto-refresh. Settings Page: New dedicated management interface with model cards, backend grid, MCP badges, and notification system for async operations. Navigation: Added Settings link to navbar, reordered menu: Home → Settings → Models → Backends. Files Changed
Review Focus Areas
ArchitectureDesign Decisions: In-memory store chosen for simplicity and zero external dependencies. Limits are hardcoded (acceptable for initial rollout). Token metrics endpoint exists but is unused (marked TODO). Backend monitoring uses gRPC where available, falls back to process stats. Scalability & Extensibility: Metrics system is intentionally single-node. Out of scope: persistence, distributed aggregation, or external metrics export beyond existing Prometheus/OpenTelemetry. Risks: Intentional: Data loss on restart (in-memory only), fixed retention windows. Unintentional: Potential memory pressure under high load if limits prove insufficient. Review pruning logic and goroutine cleanup on shutdown. Merge StatusNOT MERGEABLE — PR Score 3/100, below threshold (50)
|
| func (m *InMemoryMetricsStore) Stop() { | ||
| close(m.stopChan) |
There was a problem hiding this comment.
Stop closes stopChan without guarding against repeated calls, so a second shutdown path will panic; make Stop idempotent with sync.Once or a non-blocking close guard.
Suggested fix
type InMemoryMetricsStore struct {
endpoints map[string]int64
models map[string]int64
backends map[string]int64
timeSeries []RequestRecord
successCount int64
failureCount int64
mu sync.RWMutex
stopChan chan struct{}
stopOnce sync.Once
maxRecords int
maxMapKeys int
pruneEvery time.Duration
}
func (m *InMemoryMetricsStore) Stop() {
m.stopOnce.Do(func() {
close(m.stopChan)
})
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 168-169
Issue Type: functional-critical
Severity: critical
Issue Description:
Stop closes stopChan without guarding against repeated calls, so a second shutdown path will panic; make Stop idempotent with sync.Once or a non-blocking close guard.
Current Code:
func (m *InMemoryMetricsStore) Stop() {
close(m.stopChan)
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| summary := fiber.Map{ | ||
| "Title": "LocalAI - Settings & Management", | ||
| "Version": internal.PrintableVersion(), | ||
| "BaseURL": utils.BaseURL(c), | ||
| "Models": modelsWithoutConfig, | ||
| "ModelsConfig": modelConfigs, | ||
| "GalleryConfig": galleryConfigs, | ||
| "ApplicationConfig": appConfig, | ||
| "ProcessingModels": processingModels, | ||
| "TaskTypes": taskTypes, | ||
| "LoadedModels": loadedModelsMap, | ||
| "InstalledBackends": installedBackends, | ||
| } |
There was a problem hiding this comment.
Passing the entire application config into the view exposes server configuration to the UI, so send only the specific fields the template needs.
Suggested fix
summary := fiber.Map{
"Title": "LocalAI - Settings & Management",
"Version": internal.PrintableVersion(),
"BaseURL": utils.BaseURL(c),
"Models": modelsWithoutConfig,
"ModelsConfig": modelConfigs,
"GalleryConfig": galleryConfigs,
"ProcessingModels": processingModels,
"TaskTypes": taskTypes,
"LoadedModels": loadedModelsMap,
"InstalledBackends": installedBackends,
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/endpoints/localai/settings.go
Lines: 44-56
Issue Type: security-high
Severity: high
Issue Description:
Passing the entire application config into the view exposes server configuration to the UI, so send only the specific fields the template needs.
Current Code:
summary := fiber.Map{
"Title": "LocalAI - Settings & Management",
"Version": internal.PrintableVersion(),
"BaseURL": utils.BaseURL(c),
"Models": modelsWithoutConfig,
"ModelsConfig": modelConfigs,
"GalleryConfig": galleryConfigs,
"ApplicationConfig": appConfig,
"ProcessingModels": processingModels,
"TaskTypes": taskTypes,
"LoadedModels": loadedModelsMap,
"InstalledBackends": installedBackends,
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| } else { | ||
| // Update context locals with the model name from the request body | ||
| // This ensures downstream middleware (like metrics) can access it | ||
| ctx.Locals(CONTEXT_LOCALS_KEY_MODEL_NAME, input.ModelName(nil)) |
There was a problem hiding this comment.
The request body can overwrite the trusted model selected earlier in middleware, so only set the local when it is empty or validate it against the authorized model.
Suggested fix
} else {
// Preserve the model selected earlier in the middleware chain.
if localModelName, ok := ctx.Locals(CONTEXT_LOCALS_KEY_MODEL_NAME).(string); !ok || localModelName == "" {
ctx.Locals(CONTEXT_LOCALS_KEY_MODEL_NAME, input.ModelName(nil))
}
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/middleware/request.go
Lines: 130-133
Issue Type: security-high
Severity: high
Issue Description:
The request body can overwrite the trusted model selected earlier in middleware, so only set the local when it is empty or validate it against the authorized model.
Current Code:
} else {
// Update context locals with the model name from the request body
// This ensures downstream middleware (like metrics) can access it
ctx.Locals(CONTEXT_LOCALS_KEY_MODEL_NAME, input.ModelName(nil))
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| // Reset metrics (optional - for testing/admin purposes) | ||
| app.Post("/api/metrics/reset", func(c *fiber.Ctx) error { | ||
| metricsStore.Reset() | ||
| return c.JSON(fiber.Map{ | ||
| "success": true, | ||
| "message": "Metrics reset successfully", | ||
| }) | ||
| }) |
There was a problem hiding this comment.
The reset endpoint performs a state-changing administrative action without any local authorization check, so gate it behind an admin-only middleware or remove it from the UI API.
Suggested fix
// Reset metrics (optional - for testing/admin purposes)
app.Post("/api/metrics/reset", adminOnlyMiddleware, func(c *fiber.Ctx) error {
metricsStore.Reset()
return c.JSON(fiber.Map{
"success": true,
"message": "Metrics reset successfully",
})
})Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/routes/ui_api.go
Lines: 810-817
Issue Type: security-high
Severity: high
Issue Description:
The reset endpoint performs a state-changing administrative action without any local authorization check, so gate it behind an admin-only middleware or remove it from the UI API.
Current Code:
// Reset metrics (optional - for testing/admin purposes)
app.Post("/api/metrics/reset", func(c *fiber.Ctx) error {
metricsStore.Reset()
return c.JSON(fiber.Map{
"success": true,
"message": "Metrics reset successfully",
})
})
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| const response = await fetch(`/api/backends/system/delete/${encodeURIComponent(backendName)}`, { | ||
| method: 'POST' | ||
| }); |
There was a problem hiding this comment.
State-changing POST requests are sent without any CSRF token or same-site validation header, so add CSRF protection to each fetch call.
Also reported at: core/http/views/settings.html L501–L507, L526–L528, L556–L561
Suggested fix
const response = await fetch(`/api/backends/system/delete/${encodeURIComponent(backendName)}`, {
method: 'POST',
headers: {
'X-CSRF-Token': window.csrfToken
},
credentials: 'same-origin'
});Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert javascript developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/settings.html
Lines: 472-474
Issue Type: security-high
Severity: high
Issue Description:
State-changing POST requests are sent without any CSRF token or same-site validation header, so add CSRF protection to each fetch call.
_Also reported at: `core/http/views/settings.html` L501–L507, L526–L528, L556–L561_
Current Code:
const response = await fetch(`/api/backends/system/delete/${encodeURIComponent(backendName)}`, {
method: 'POST'
});
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow javascript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| <i class="fas fa-plus mr-3 text-lg"></i> | ||
| <span>Import Model</span> | ||
| <i class="fas fa-upload ml-3 opacity-70 group-hover:opacity-100 transition-opacity"></i> | ||
| <a href="https://localai.io/basics/getting_started/" target="_blank" class="group inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 transform hover:scale-105"> |
There was a problem hiding this comment.
Links opened with target="_blank" should include rel="noopener noreferrer" to prevent reverse tabnabbing.
Also reported at: core/http/views/index.html L238, L250
| <a href="https://localai.io/basics/getting_started/" target="_blank" class="group inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 transform hover:scale-105"> | |
| <a href="https://localai.io/basics/getting_started/" target="_blank" rel="noopener noreferrer" class="group inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 transform hover:scale-105"> |
Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert html developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/index.html
Lines: 37-37
Issue Type: security-medium
Severity: medium
Issue Description:
Links opened with target="_blank" should include rel="noopener noreferrer" to prevent reverse tabnabbing.
_Also reported at: `core/http/views/index.html` L238, L250_
Current Code:
<a href="https://localai.io/basics/getting_started/" target="_blank" class="group inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 transform hover:scale-105">
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow html best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| </div> | ||
|
|
||
| <!-- Chart.js --> | ||
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> |
There was a problem hiding this comment.
The CDN script should use Subresource Integrity and crossorigin attributes or be self-hosted to reduce supply chain risk.
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js" integrity="YOUR_SRI_HASH" crossorigin="anonymous"></script> |
Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert html developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/index.html
Lines: 270-270
Issue Type: security-medium
Severity: medium
Issue Description:
The CDN script should use Subresource Integrity and crossorigin attributes or be self-hosted to reduce supply chain risk.
Current Code:
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow html best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| // Try to extract from JSON body for POST requests | ||
| if c.Method() == fiber.MethodPost { | ||
| // Read body | ||
| bodyBytes := c.Body() | ||
| if len(bodyBytes) > 0 { | ||
| // Parse JSON | ||
| var reqBody map[string]interface{} | ||
| if err := json.Unmarshal(bodyBytes, &reqBody); err == nil { | ||
| if modelVal, ok := reqBody["model"]; ok { | ||
| if modelStr, ok := modelVal.(string); ok { | ||
| return modelStr | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Parsing the full request body again in middleware adds avoidable CPU and memory cost on every POST request, so prefer reusing the model already extracted by earlier middleware or gate this fallback behind endpoint checks and content type validation.
Suggested fix
// Try to extract from JSON body only for JSON POST requests on known model-carrying endpoints
if c.Method() == fiber.MethodPost && strings.HasPrefix(c.Get("Content-Type"), fiber.MIMEApplicationJSON) {
if shouldInspectBodyForModel(c.Path()) {
bodyBytes := c.Body()
if len(bodyBytes) > 0 {
var reqBody struct {
Model string `json:"model"`
}
if err := json.Unmarshal(bodyBytes, &reqBody); err == nil && reqBody.Model != "" {
return reqBody.Model
}
}
}
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/middleware/metrics.go
Lines: 150-163
Issue Type: performance-medium
Severity: medium
Issue Description:
Parsing the full request body again in middleware adds avoidable CPU and memory cost on every POST request, so prefer reusing the model already extracted by earlier middleware or gate this fallback behind endpoint checks and content type validation.
Current Code:
// Try to extract from JSON body for POST requests
if c.Method() == fiber.MethodPost {
// Read body
bodyBytes := c.Body()
if len(bodyBytes) > 0 {
// Parse JSON
var reqBody map[string]interface{}
if err := json.Unmarshal(bodyBytes, &reqBody); err == nil {
if modelVal, ok := reqBody["model"]; ok {
if modelStr, ok := modelVal.(string); ok {
return modelStr
}
}
}
}
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
Security Scan Summary
Consider addressing security findings before merging Scan completed in 35.5s View vulnerability details (1 items)1. VAR_IN_HREF (CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')) HIGH File:
Fix: Review and fix the security issue following OWASP guidelines. Security scan powered by Codity.ai |
Dependency vulnerability scanning
View vulnerability details (4 items)1. pip 24.0 CVE: GHSA-4xh5-x5gv-qwph
2. pip 24.0 CVE: GHSA-6vgw-5pg2-w6jp
3. pip 24.0 CVE: GHSA-58qw-9mgm-455v
4. pip 24.0 CVE: GHSA-jp4c-xjxw-mgf9
Powered by Codity.ai · Docs |
License Compliance Scan
Weak copyleft licenses found - verify compatibility Some packages have unknown licenses - manual review required Medium Risk Licenses - 4 packagesMPL-2.0 (4 packages):
Unknown Licenses - 54 packages
...and 34 more Powered by Codity.ai · Docs |
Code Quality Report — test-org-codity/LocalAI · PR #4Scanned: 2026-05-19 16:08 UTC | Score: 5/100 | Provider: github Executive Summary
Top Findings[CQ-LLM-002]
|
| File | Critical | High | Medium | Low | Total |
|---|---|---|---|---|---|
core/http/app.go |
0 | 0 | 1 | 1 | 2 |
core/http/endpoints/localai/settings.go |
0 | 1 | 1 | 0 | 2 |
core/http/middleware/metrics.go |
0 | 0 | 1 | 2 | 3 |
core/http/routes/ui_api.go |
0 | 0 | 0 | 2 | 2 |
core/http/views/index.html |
0 | 0 | 1 | 309 | 310 |
core/http/views/partials/navbar.html |
0 | 0 | 0 | 7 | 7 |
core/http/views/settings.html |
0 | 0 | 0 | 454 | 454 |
core/services/metrics.go |
0 | 0 | 0 | 2 | 2 |
Recommendations
- Resolve High severity issues, especially error handling gaps and performance bottlenecks.
- Run automated tests after applying fixes to verify no regressions.
|
@codity review |
Policy Check Failed✗ 3/3 policy checks failed: • Need 2 more approval(s) (0/2) — comment LGTM or approve via review To merge this PR:
|
PR SummaryWhat Changed
Key Changes by AreaMetrics & Monitoring
UI/UX
Files Changed
Review Focus Areas
ArchitectureDesign Decisions
Scalability & Extensibility
Risks
Merge StatusNOT MERGEABLE — PR Score 3/100, below threshold (50)
|
| // Stop gracefully shuts down the metrics store | ||
| func (m *InMemoryMetricsStore) Stop() { | ||
| close(m.stopChan) |
There was a problem hiding this comment.
Stop closes stopChan unconditionally, so a second Stop call will panic; guard the close with sync.Once or a non-blocking select.
Suggested fix
type InMemoryMetricsStore struct {
endpoints map[string]int64
models map[string]int64
backends map[string]int64
timeSeries []RequestRecord
successCount int64
failureCount int64
mu sync.RWMutex
stopChan chan struct{}
stopOnce sync.Once
maxRecords int
maxMapKeys int
pruneEvery time.Duration
}
func (m *InMemoryMetricsStore) Stop() {
m.stopOnce.Do(func() {
close(m.stopChan)
})
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 167-169
Issue Type: functional-critical
Severity: critical
Issue Description:
Stop closes stopChan unconditionally, so a second Stop call will panic; guard the close with sync.Once or a non-blocking select.
Current Code:
func (m *InMemoryMetricsStore) Stop() {
close(m.stopChan)
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| "Models": modelsWithoutConfig, | ||
| "ModelsConfig": modelConfigs, | ||
| "GalleryConfig": galleryConfigs, | ||
| "ApplicationConfig": appConfig, |
There was a problem hiding this comment.
Passing the entire application config into the template exposes all server settings to the UI, so only include the specific non-sensitive fields the view needs.
Suggested fix
"ApplicationConfig": fiber.Map{
"SomeSafeField": appConfig.SomeSafeField,
},Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/endpoints/localai/settings.go
Lines: 51-51
Issue Type: security-high
Severity: high
Issue Description:
Passing the entire application config into the template exposes all server settings to the UI, so only include the specific non-sensitive fields the view needs.
Current Code:
"ApplicationConfig": appConfig,
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| app.Get("/", localai.WelcomeEndpoint(appConfig, cl, ml, processingOps)) | ||
|
|
||
| // Settings page - detailed model/backend management | ||
| app.Get("/settings", localai.SettingsEndpoint(appConfig, cl, ml, processingOps)) |
There was a problem hiding this comment.
The new settings UI exposes ApplicationConfig to the template, which likely includes sensitive fields such as P2P tokens, so gate this route with authentication or pass only a sanitized view model.
| app.Get("/settings", localai.SettingsEndpoint(appConfig, cl, ml, processingOps)) | |
| app.Get("/settings", middleware.RequireAuth(), localai.SettingsEndpoint(sanitizedConfig, cl, ml, processingOps)) |
Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/routes/ui.go
Lines: 27-27
Issue Type: security-high
Severity: high
Issue Description:
The new settings UI exposes `ApplicationConfig` to the template, which likely includes sensitive fields such as P2P tokens, so gate this route with authentication or pass only a sanitized view model.
Current Code:
app.Get("/settings", localai.SettingsEndpoint(appConfig, cl, ml, processingOps))
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| hours := 24 | ||
| if hoursParam := c.Query("hours"); hoursParam != "" { | ||
| if h, err := strconv.Atoi(hoursParam); err == nil && h > 0 { | ||
| hours = h | ||
| } | ||
| } | ||
|
|
||
| timeSeries := metricsStore.GetRequestsOverTime(hours) |
There was a problem hiding this comment.
The hours query parameter is accepted without an upper bound, so clamp it to a sane maximum before querying the metrics store.
Suggested fix
hours := 24
if hoursParam := c.Query("hours"); hoursParam != "" {
if h, err := strconv.Atoi(hoursParam); err == nil && h > 0 {
if h > 168 {
h = 168
}
hours = h
}
}
timeSeries := metricsStore.GetRequestsOverTime(hours)Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/routes/ui_api.go
Lines: 796-803
Issue Type: robustness-medium
Severity: medium
Issue Description:
The hours query parameter is accepted without an upper bound, so clamp it to a sane maximum before querying the metrics store.
Current Code:
hours := 24
if hoursParam := c.Query("hours"); hoursParam != "" {
if h, err := strconv.Atoi(hoursParam); err == nil && h > 0 {
hours = h
}
}
timeSeries := metricsStore.GetRequestsOverTime(hours)
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| <i class="fas fa-plus mr-3 text-lg"></i> | ||
| <span>Import Model</span> | ||
| <i class="fas fa-upload ml-3 opacity-70 group-hover:opacity-100 transition-opacity"></i> | ||
| <a href="https://localai.io/basics/getting_started/" target="_blank" class="group inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 transform hover:scale-105"> |
There was a problem hiding this comment.
External links opened with target="_blank" should also set rel="noopener noreferrer" to prevent reverse tabnabbing.
Also reported at: core/http/views/index.html L238, L250
| <a href="https://localai.io/basics/getting_started/" target="_blank" class="group inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 transform hover:scale-105"> | |
| <a href="https://localai.io/basics/getting_started/" target="_blank" rel="noopener noreferrer" class="group inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 transform hover:scale-105"> |
Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert html developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/index.html
Lines: 37-37
Issue Type: security-medium
Severity: medium
Issue Description:
External links opened with target="_blank" should also set rel="noopener noreferrer" to prevent reverse tabnabbing.
_Also reported at: `core/http/views/index.html` L238, L250_
Current Code:
<a href="https://localai.io/basics/getting_started/" target="_blank" class="group inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 transform hover:scale-105">
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow html best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| </div> | ||
|
|
||
| <!-- Chart.js --> | ||
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> |
There was a problem hiding this comment.
Loading Chart.js from a CDN without an integrity attribute weakens script supply chain protection, so add SRI and crossorigin or self-host the asset.
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js" integrity="<SRI_HASH>" crossorigin="anonymous"></script> |
Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert html developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/index.html
Lines: 270-270
Issue Type: security-medium
Severity: medium
Issue Description:
Loading Chart.js from a CDN without an integrity attribute weakens script supply chain protection, so add SRI and crossorigin or self-host the asset.
Current Code:
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow html best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
Security Scan Summary
No critical security issues detected Scan completed in 46.4sSecurity scan powered by Codity.ai |
Dependency vulnerability scanning
View vulnerability details (4 items)1. pip 24.0 CVE: GHSA-4xh5-x5gv-qwph
2. pip 24.0 CVE: GHSA-6vgw-5pg2-w6jp
3. pip 24.0 CVE: GHSA-58qw-9mgm-455v
4. pip 24.0 CVE: GHSA-jp4c-xjxw-mgf9
Powered by Codity.ai · Docs |
License Compliance Scan
Weak copyleft licenses found - verify compatibility Some packages have unknown licenses - manual review required Medium Risk Licenses - 4 packagesMPL-2.0 (4 packages):
Unknown Licenses - 37 packages
...and 17 more Powered by Codity.ai · Docs |
Code Quality Report — test-org-codity/LocalAI · PR #4Scanned: 2026-05-19 16:45 UTC | Score: 5/100 | Provider: github Executive Summary
Top Findings[CQ-LLM-002]
|
| File | Critical | High | Medium | Low | Total |
|---|---|---|---|---|---|
core/http/app.go |
0 | 0 | 1 | 1 | 2 |
core/http/endpoints/localai/settings.go |
0 | 1 | 0 | 1 | 2 |
core/http/middleware/metrics.go |
0 | 0 | 3 | 1 | 4 |
core/http/routes/ui_api.go |
0 | 0 | 0 | 2 | 2 |
core/http/views/index.html |
0 | 0 | 1 | 309 | 310 |
core/http/views/partials/navbar.html |
0 | 0 | 0 | 7 | 7 |
core/http/views/settings.html |
0 | 0 | 0 | 454 | 454 |
core/services/metrics.go |
0 | 0 | 0 | 2 | 2 |
Recommendations
- Resolve High severity issues, especially error handling gaps and performance bottlenecks.
- Run automated tests after applying fixes to verify no regressions.
|
@codity review |
Policy Check Failed✗ 3/3 policy checks failed: • Need 2 more approval(s) (0/2) — comment LGTM or approve via review To merge this PR:
|
PR SummaryWhat Changed
Key Changes by AreaMetrics System: API Endpoints: New UI/UX: Dashboard now shows conditional welcome wizard vs. metrics view. Settings page consolidates model management with toast notifications, backend deletion, and reload functionality. Navigation: Added Settings link to navbar; standardized Alpine.js component names across views. Files Changed
Review Focus Areas
ArchitectureDesign Decisions: In-memory storage keeps metrics simple and avoids external dependencies. Time-series pruning trades historical precision for bounded memory. UI routes excluded from metrics to focus on actual API usage. Scalability & Extensibility: Metrics store is interface-based ( Risks: Metrics data is lost on restart (intentional, acceptable for monitoring). Background pruning goroutine adds minimal overhead but could delay shutdown if stuck. No authentication on Merge StatusNOT MERGEABLE — PR Score 3/100, below threshold (50)
|
| loadedModelsMap[m.ID] = true | ||
| } | ||
|
|
||
| modelsWithoutConfig, _ := services.ListModels(cl, ml, config.NoFilterFn, services.LOOSE_ONLY) |
There was a problem hiding this comment.
The error from ListModels is ignored and should be handled to avoid rendering a partial or misleading settings page.
Suggested fix
modelsWithoutConfig, err := services.ListModels(cl, ml, config.NoFilterFn, services.LOOSE_ONLY)
if err != nil {
return err
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/endpoints/localai/settings.go
Lines: 39-39
Issue Type: robustness-high
Severity: high
Issue Description:
The error from ListModels is ignored and should be handled to avoid rendering a partial or misleading settings page.
Current Code:
modelsWithoutConfig, _ := services.ListModels(cl, ml, config.NoFilterFn, services.LOOSE_ONLY)
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| app.Post("/api/metrics/reset", func(c *fiber.Ctx) error { | ||
| metricsStore.Reset() | ||
| return c.JSON(fiber.Map{ | ||
| "success": true, | ||
| "message": "Metrics reset successfully", | ||
| }) |
There was a problem hiding this comment.
The metrics reset endpoint performs a state-changing administrative action without any route-level authorization check, so add an explicit admin guard or remove this endpoint from the UI API.
Suggested fix
app.Post("/api/metrics/reset", func(c *fiber.Ctx) error {
if !isAdminRequest(c) {
return c.SendStatus(fiber.StatusForbidden)
}
metricsStore.Reset()
return c.JSON(fiber.Map{
"success": true,
"message": "Metrics reset successfully",
})
})Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/routes/ui_api.go
Lines: 811-816
Issue Type: security-high
Severity: high
Issue Description:
The metrics reset endpoint performs a state-changing administrative action without any route-level authorization check, so add an explicit admin guard or remove this endpoint from the UI API.
Current Code:
app.Post("/api/metrics/reset", func(c *fiber.Ctx) error {
metricsStore.Reset()
return c.JSON(fiber.Map{
"success": true,
"message": "Metrics reset successfully",
})
})
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| <i class="fas fa-arrow-left ml-3 opacity-70 group-hover:opacity-100 transition-opacity"></i> | ||
| </a> | ||
|
|
||
| <a href="https://localai.io" target="_blank" |
There was a problem hiding this comment.
Links opened with target="_blank" should include rel="noopener noreferrer" to prevent reverse tabnabbing.
Also reported at: core/http/views/settings.html L407
Suggested fix
<a href="https://localai.io" target="_blank" rel="noopener noreferrer"Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert html developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/settings.html
Lines: 60-60
Issue Type: security-high
Severity: high
Issue Description:
Links opened with target="_blank" should include rel="noopener noreferrer" to prevent reverse tabnabbing.
_Also reported at: `core/http/views/settings.html` L407_
Current Code:
<a href="https://localai.io" target="_blank"
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow html best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| // Stop gracefully shuts down the metrics store | ||
| func (m *InMemoryMetricsStore) Stop() { | ||
| close(m.stopChan) |
There was a problem hiding this comment.
Stop closes stopChan unconditionally and will panic if called more than once, so guard the close with sync.Once or a non-blocking select.
Suggested fix
type InMemoryMetricsStore struct {
endpoints map[string]int64
models map[string]int64
backends map[string]int64
timeSeries []RequestRecord
successCount int64
failureCount int64
mu sync.RWMutex
stopChan chan struct{}
stopOnce sync.Once
maxRecords int
maxMapKeys int
pruneEvery time.Duration
}
func (m *InMemoryMetricsStore) Stop() {
m.stopOnce.Do(func() {
close(m.stopChan)
})
}Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/services/metrics.go
Lines: 167-169
Issue Type: robustness-high
Severity: high
Issue Description:
Stop closes stopChan unconditionally and will panic if called more than once, so guard the close with sync.Once or a non-blocking select.
Current Code:
func (m *InMemoryMetricsStore) Stop() {
close(m.stopChan)
}
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| app.Get("/api/metrics/summary", func(c *fiber.Ctx) error { | ||
| endpointStats := metricsStore.GetEndpointStats() | ||
| modelStats := metricsStore.GetModelStats() | ||
| backendStats := metricsStore.GetBackendStats() | ||
|
|
||
| // Get top 5 models | ||
| type modelStat struct { | ||
| Name string `json:"name"` | ||
| Count int64 `json:"count"` | ||
| } | ||
| topModels := make([]modelStat, 0) | ||
| for model, count := range modelStats { | ||
| topModels = append(topModels, modelStat{Name: model, Count: count}) | ||
| } | ||
| sort.Slice(topModels, func(i, j int) bool { | ||
| return topModels[i].Count > topModels[j].Count | ||
| }) | ||
| if len(topModels) > 5 { | ||
| topModels = topModels[:5] | ||
| } | ||
|
|
||
| // Get top 5 endpoints | ||
| type endpointStat struct { | ||
| Name string `json:"name"` | ||
| Count int64 `json:"count"` | ||
| } | ||
| topEndpoints := make([]endpointStat, 0) | ||
| for endpoint, count := range endpointStats { | ||
| topEndpoints = append(topEndpoints, endpointStat{Name: endpoint, Count: count}) | ||
| } | ||
| sort.Slice(topEndpoints, func(i, j int) bool { | ||
| return topEndpoints[i].Count > topEndpoints[j].Count | ||
| }) | ||
| if len(topEndpoints) > 5 { | ||
| topEndpoints = topEndpoints[:5] | ||
| } | ||
|
|
||
| return c.JSON(fiber.Map{ | ||
| "totalRequests": metricsStore.GetTotalRequests(), | ||
| "successRate": metricsStore.GetSuccessRate(), | ||
| "topModels": topModels, | ||
| "topEndpoints": topEndpoints, | ||
| "topBackends": backendStats, | ||
| }) | ||
| }) | ||
|
|
||
| // Get endpoint statistics | ||
| app.Get("/api/metrics/endpoints", func(c *fiber.Ctx) error { | ||
| stats := metricsStore.GetEndpointStats() | ||
| return c.JSON(fiber.Map{ | ||
| "endpoints": stats, | ||
| }) | ||
| }) | ||
|
|
||
| // Get model statistics | ||
| app.Get("/api/metrics/models", func(c *fiber.Ctx) error { | ||
| stats := metricsStore.GetModelStats() | ||
| return c.JSON(fiber.Map{ | ||
| "models": stats, | ||
| }) | ||
| }) | ||
|
|
||
| // Get backend statistics | ||
| app.Get("/api/metrics/backends", func(c *fiber.Ctx) error { | ||
| stats := metricsStore.GetBackendStats() | ||
| return c.JSON(fiber.Map{ | ||
| "backends": stats, | ||
| }) | ||
| }) | ||
|
|
||
| // Get time series data | ||
| app.Get("/api/metrics/timeseries", func(c *fiber.Ctx) error { | ||
| // Default to last 24 hours | ||
| hours := 24 | ||
| if hoursParam := c.Query("hours"); hoursParam != "" { | ||
| if h, err := strconv.Atoi(hoursParam); err == nil && h > 0 { | ||
| hours = h | ||
| } | ||
| } | ||
|
|
||
| timeSeries := metricsStore.GetRequestsOverTime(hours) | ||
| return c.JSON(fiber.Map{ | ||
| "timeseries": timeSeries, | ||
| "hours": hours, | ||
| }) |
There was a problem hiding this comment.
These endpoints expose aggregate usage and backend metadata through the web UI API, so restrict them to privileged users or omit sensitive fields.
Suggested fix
app.Get("/api/metrics/summary", func(c *fiber.Ctx) error {
if !isAdminRequest(c) {
return c.SendStatus(fiber.StatusForbidden)
}
endpointStats := metricsStore.GetEndpointStats()
modelStats := metricsStore.GetModelStats()
backendStats := metricsStore.GetBackendStats()
...
})Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert go developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/routes/ui_api.go
Lines: 723-807
Issue Type: security-medium
Severity: medium
Issue Description:
These endpoints expose aggregate usage and backend metadata through the web UI API, so restrict them to privileged users or omit sensitive fields.
Current Code:
app.Get("/api/metrics/summary", func(c *fiber.Ctx) error {
endpointStats := metricsStore.GetEndpointStats()
modelStats := metricsStore.GetModelStats()
backendStats := metricsStore.GetBackendStats()
...
})
...
app.Get("/api/metrics/endpoints", func(c *fiber.Ctx) error {
stats := metricsStore.GetEndpointStats()
return c.JSON(fiber.Map{
"endpoints": stats,
})
})
...
app.Get("/api/metrics/models", func(c *fiber.Ctx) error {
stats := metricsStore.GetModelStats()
return c.JSON(fiber.Map{
"models": stats,
})
})
...
app.Get("/api/metrics/backends", func(c *fiber.Ctx) error {
stats := metricsStore.GetBackendStats()
return c.JSON(fiber.Map{
"backends": stats,
})
})
...
app.Get("/api/metrics/timeseries", func(c *fiber.Ctx) error {
...
return c.JSON(fiber.Map{
"timeseries": timeSeries,
"hours": hours,
})
})
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow go best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| <i class="fas fa-plus mr-3 text-lg"></i> | ||
| <span>Import Model</span> | ||
| <i class="fas fa-upload ml-3 opacity-70 group-hover:opacity-100 transition-opacity"></i> | ||
| <a href="https://localai.io/basics/getting_started/" target="_blank" class="group inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 transform hover:scale-105"> |
There was a problem hiding this comment.
Opening a new tab with target="_blank" should include rel="noopener noreferrer" to prevent reverse tabnabbing.
Also reported at: core/http/views/index.html L238, L250
| <a href="https://localai.io/basics/getting_started/" target="_blank" class="group inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 transform hover:scale-105"> | |
| <a href="https://localai.io/basics/getting_started/" target="_blank" rel="noopener noreferrer" class="group inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 transform hover:scale-105"> |
Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert html developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/index.html
Lines: 37-37
Issue Type: security-medium
Severity: medium
Issue Description:
Opening a new tab with target="_blank" should include rel="noopener noreferrer" to prevent reverse tabnabbing.
_Also reported at: `core/http/views/index.html` L238, L250_
Current Code:
<a href="https://localai.io/basics/getting_started/" target="_blank" class="group inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-4 px-8 rounded-xl font-semibold text-lg transition-all duration-300 transform hover:scale-105">
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow html best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
| {{ if gt (len .ModelsConfig) 0 }} | ||
| this.fetchMetrics(); | ||
| // Auto-refresh every 30 seconds | ||
| setInterval(() => this.fetchMetrics(), 30000); |
There was a problem hiding this comment.
The polling interval created in init() is never cleaned up, so repeated component mounts can leak timers; store the interval ID and clear it on teardown.
Suggested fix
this.metricsInterval = setInterval(() => this.fetchMetrics(), 30000);Prompt for AI assistance
Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:
You are an expert javascript developer with deep knowledge of security, performance, and best practices.
### Context
File: core/http/views/index.html
Lines: 289-289
Issue Type: performance-medium
Severity: medium
Issue Description:
The polling interval created in init() is never cleaned up, so repeated component mounts can leak timers; store the interval ID and clear it on teardown.
Current Code:
setInterval(() => this.fetchMetrics(), 30000);
---
### Instructions
1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow javascript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed
### Constraints
- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready
---
Security Scan Summary
No critical security issues detected Scan completed in 43.8sSecurity scan powered by Codity.ai |
Dependency vulnerability scanning
View vulnerability details (4 items)1. pip 24.0 CVE: GHSA-4xh5-x5gv-qwph
2. pip 24.0 CVE: GHSA-6vgw-5pg2-w6jp
3. pip 24.0 CVE: GHSA-58qw-9mgm-455v
4. pip 24.0 CVE: GHSA-jp4c-xjxw-mgf9
Powered by Codity.ai · Docs |
License Compliance Scan
Weak copyleft licenses found - verify compatibility Some packages have unknown licenses - manual review required Medium Risk Licenses - 4 packagesMPL-2.0 (4 packages):
Unknown Licenses - 73 packages
...and 53 more Powered by Codity.ai · Docs |
Code Quality Report — test-org-codity/LocalAI · PR #4Scanned: 2026-05-21 12:35 UTC | Score: 5/100 | Provider: github Executive Summary
Top Findings[CQ-LLM-002]
|
| File | Critical | High | Medium | Low | Total |
|---|---|---|---|---|---|
core/http/app.go |
0 | 0 | 1 | 1 | 2 |
core/http/endpoints/localai/settings.go |
0 | 1 | 2 | 0 | 3 |
core/http/middleware/metrics.go |
0 | 0 | 1 | 2 | 3 |
core/http/routes/ui_api.go |
0 | 0 | 0 | 2 | 2 |
core/http/views/index.html |
0 | 0 | 1 | 309 | 310 |
core/http/views/partials/navbar.html |
0 | 0 | 0 | 7 | 7 |
core/http/views/settings.html |
0 | 0 | 0 | 454 | 454 |
core/services/metrics.go |
0 | 0 | 0 | 2 | 2 |
Recommendations
- Resolve High severity issues, especially error handling gaps and performance bottlenecks.
- Run automated tests after applying fixes to verify no regressions.
No description provided.