Skip to content

Fix/tools null properties#1

Open
DhirenMhatre wants to merge 3 commits into
masterfrom
fix/tools-null-properties
Open

Fix/tools null properties#1
DhirenMhatre wants to merge 3 commits into
masterfrom
fix/tools-null-properties

Conversation

@DhirenMhatre
Copy link
Copy Markdown

No description provided.

mudler added 3 commits January 4, 2026 20:22
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Policy Check Failed

✗ 3/3 policy checks failed:

• Need 2 more approval(s) (0/2) — comment LGTM or approve via review
• Missing ticket reference (expected: JIRA-, ENG-, #*)
• 5 code file(s) changed but no test files added


To merge this PR:

  1. Address the failed checks listed above
  2. Ensure branch protection requires the codity/policy-check status

Configure policies in your dashboard

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

PR review started! Estimated time: 5-10 minutes.
Using default review instructions (no custom configuration found)

Learn More

View Analytics Dashboard

Ask Codity questions: Mention @codity {your question} in a comment to get answers about the code.

Trigger a manual review: Comment @codity review on a PR or MR.

Generate unit tests: Comment /generate-tests to auto-generate tests for Go, Python, Ruby, JavaScript, TypeScript, and Java files.

Run security scan again: Comment /security-scan to run SAST and dependency vulnerability scans for all major languages in your repo.

View Full Docs

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 16, 2026

Greptile Summary

This PR introduces a SanitizeTools helper that walks tool definitions before serialization and converts null JSON values to empty objects, aimed at preventing Jinja template errors in the llama.cpp backend when tool parameter schemas contain null entries.

  • SanitizeTools applies its nil-to-{} conversion to the entire function object, not only parameters.properties, so a Function with Parameters: nil has its parameters field changed from null to {} in the serialized JSON — a wire-format change for tools without parameters that the accompanying test incorrectly asserts will not happen.
  • Both call sites (chat.go and inference.go) pass tools through five JSON passes inside SanitizeTools and then immediately marshal the result again, totalling six serialization round-trips per inference call that carries tools.
  • Two comment lines added to grpc-server.cpp describe a C++ sanitization function that was never written, leaving orphaned comments unattached to any code.

Confidence Score: 3/5

Merging as-is will silently alter the wire format for tools that have no parameters and will cause the nil-parameters test to fail.

The sanitization logic applies to the entire function object rather than only the null-containing properties, so Functions with no parameters have their Parameters field changed from nil to an empty map, mutating the serialized JSON from parameters:null to parameters:{}, which could affect downstream template rendering.

pkg/functions/functions.go and pkg/functions/functions_test.go need the most attention — the former has the logic gap and the latter has a test assertion that contradicts the implementation's actual behavior.

Important Files Changed

Filename Overview
pkg/functions/functions.go Adds SanitizeTools and sanitizeValue helpers; sanitization applied to entire function object incorrectly converts nil Parameters to empty map, changing wire format from null to {}
pkg/functions/functions_test.go Adds SanitizeTools tests; the nil-parameters test will fail because the implementation returns a non-nil empty map instead of nil
core/http/endpoints/openai/chat.go Wires SanitizeTools into the chat endpoint before serializing tools to JSON; otherwise correct
core/http/endpoints/openai/inference.go Wires SanitizeTools into ComputeChoices before serializing tools to JSON; same pattern as chat.go, otherwise correct
backend/cpp/llama-cpp/grpc-server.cpp Adds two dangling comment lines with no associated implementation, leaving misleading dead comments between parse_options and kv_cache_types

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Incoming OpenAI Request with Tools] --> B{len tools > 0?}
    B -- No --> Z[toolsJSON = empty string]
    B -- Yes --> C[functions.SanitizeTools called]
    subgraph SanitizeTools[SanitizeTools - 5 JSON passes]
        C1[Pass 1 - Marshal Tools to JSON] --> C2[Pass 2 - Unmarshal to generic map]
        C2 --> C3[Iterate each tool]
        C3 --> C4[sanitizeValue on ENTIRE function object]
        C4 --> C5{value nil?}
        C5 -- Yes --> C6[Convert nil to empty map]
        C5 -- No map --> C7[Recurse into map keys]
        C5 -- No slice --> C8[Recurse into slice elements]
        C5 -- No primitive --> C9[Return as-is]
        C6 --> C10[Pass 3 - Marshal sanitized map to JSON]
        C7 --> C10
        C8 --> C10
        C9 --> C10
        C10 --> C11[Pass 4 and 5 - Unmarshal JSON back to Tools struct]
    end
    C11 --> D[Pass 6 - json.Marshal sanitizedTools in caller]
    D --> E[toolsJSON string sent to backend inference]
Loading

Reviews (1): Last reviewed commit: "recursive" | Re-trigger Greptile

Comment on lines +109 to +193
func sanitizeValue(value interface{}, path string) interface{} {
if value == nil {
// Convert null to empty object
xlog.Debug("SanitizeTools: found null value, converting to empty object", "path", path)
return map[string]interface{}{}
}

switch v := value.(type) {
case map[string]interface{}:
// Recursively sanitize map values
sanitized := make(map[string]interface{})
for key, val := range v {
newPath := path
if newPath != "" {
newPath += "."
}
newPath += key
sanitized[key] = sanitizeValue(val, newPath)
}
return sanitized

case []interface{}:
// Recursively sanitize slice elements
sanitized := make([]interface{}, len(v))
for i, val := range v {
newPath := fmt.Sprintf("%s[%d]", path, i)
sanitized[i] = sanitizeValue(val, newPath)
}
return sanitized

default:
// For primitive types (string, number, bool), return as-is
return value
}
}

// SanitizeTools removes null values from tool.parameters.properties and converts them to empty objects.
// This prevents Jinja template errors when processing tools with malformed parameter schemas.
// It works by marshaling to JSON, recursively sanitizing the JSON structure, and unmarshaling back.
func SanitizeTools(tools Tools) Tools {
if len(tools) == 0 {
return tools
}

xlog.Debug("SanitizeTools: processing tools", "count", len(tools))

// Marshal to JSON to work with the actual JSON representation
toolsJSON, err := json.Marshal(tools)
if err != nil {
xlog.Warn("SanitizeTools: failed to marshal tools to JSON", "error", err)
return tools
}

// Parse JSON into a generic structure
var toolsData []map[string]interface{}
if err := json.Unmarshal(toolsJSON, &toolsData); err != nil {
xlog.Warn("SanitizeTools: failed to unmarshal tools JSON", "error", err)
return tools
}

// Recursively sanitize the JSON structure
for i, tool := range toolsData {
if function, ok := tool["function"].(map[string]interface{}); ok {
// Recursively sanitize the entire tool structure
tool["function"] = sanitizeValue(function, fmt.Sprintf("tools[%d].function", i))
}
toolsData[i] = tool
}

// Marshal back to JSON
sanitizedJSON, err := json.Marshal(toolsData)
if err != nil {
xlog.Warn("SanitizeTools: failed to marshal sanitized tools", "error", err)
return tools
}

// Unmarshal back into Tools structure
var sanitized Tools
if err := json.Unmarshal(sanitizedJSON, &sanitized); err != nil {
xlog.Warn("SanitizeTools: failed to unmarshal sanitized tools", "error", err)
return tools
}

return sanitized
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 nil Parameters silently mutated to empty map

sanitizeValue is called on the entire function map, so when Function.Parameters is nil it is serialized to JSON null, then converted to map[string]interface{}{} by the nil-branch, and finally unmarshaled back as a non-nil empty map. The result is "parameters": {} instead of "parameters": null, which changes the wire format for every tool that has no parameters. The test "handles tools without parameters field" asserts Parameters.BeNil() but that assertion will fail under the current implementation — the value returned is an empty (non-nil) map.

Comment on lines +296 to +297
// Sanitize tools JSON to remove null values from tool.parameters.properties
// This prevents Jinja template errors when processing tools with malformed parameter schemas
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Orphaned comment with no associated implementation

These two comment lines describe sanitization logic that was never implemented in this file. They sit between the closing brace of parse_options and the kv_cache_types vector definition, unattached to any function. A future reader will look for the C++ implementation referenced by the comment and not find it, which could cause confusion about where null-sanitization actually occurs.

Comment on lines +145 to +193
// SanitizeTools removes null values from tool.parameters.properties and converts them to empty objects.
// This prevents Jinja template errors when processing tools with malformed parameter schemas.
// It works by marshaling to JSON, recursively sanitizing the JSON structure, and unmarshaling back.
func SanitizeTools(tools Tools) Tools {
if len(tools) == 0 {
return tools
}

xlog.Debug("SanitizeTools: processing tools", "count", len(tools))

// Marshal to JSON to work with the actual JSON representation
toolsJSON, err := json.Marshal(tools)
if err != nil {
xlog.Warn("SanitizeTools: failed to marshal tools to JSON", "error", err)
return tools
}

// Parse JSON into a generic structure
var toolsData []map[string]interface{}
if err := json.Unmarshal(toolsJSON, &toolsData); err != nil {
xlog.Warn("SanitizeTools: failed to unmarshal tools JSON", "error", err)
return tools
}

// Recursively sanitize the JSON structure
for i, tool := range toolsData {
if function, ok := tool["function"].(map[string]interface{}); ok {
// Recursively sanitize the entire tool structure
tool["function"] = sanitizeValue(function, fmt.Sprintf("tools[%d].function", i))
}
toolsData[i] = tool
}

// Marshal back to JSON
sanitizedJSON, err := json.Marshal(toolsData)
if err != nil {
xlog.Warn("SanitizeTools: failed to marshal sanitized tools", "error", err)
return tools
}

// Unmarshal back into Tools structure
var sanitized Tools
if err := json.Unmarshal(sanitizedJSON, &sanitized); err != nil {
xlog.Warn("SanitizeTools: failed to unmarshal sanitized tools", "error", err)
return tools
}

return sanitized
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Redundant serialization round-trip on every inference call

SanitizeTools internally performs marshal → unmarshal → sanitize → marshal → unmarshal (5 JSON passes), and both callers (chat.go and inference.go) immediately call json.Marshal on the returned value, adding a 6th pass on every inference request that carries tools. The same effect could be achieved with a single pass by walking the Tools struct directly and deep-copying only the affected fields, avoiding unnecessary allocations on the hot path.

@DhirenMhatre
Copy link
Copy Markdown
Author

@codity review

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Policy Check Failed

✗ 3/3 policy checks failed:

• Need 2 more approval(s) (0/2) — comment LGTM or approve via review
• Missing ticket reference (expected: JIRA-, ENG-, #*)
• 5 code file(s) changed but no test files added


To merge this PR:

  1. Address the failed checks listed above
  2. Ensure branch protection requires the codity/policy-check status

Configure policies in your dashboard

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

PR Summary

What Changed

  • Added SanitizeTools to convert null values in tool parameter schemas to empty objects, preventing Jinja template errors during LLM tool calling.
  • Integrated sanitization into OpenAI chat and inference endpoints before JSON serialization.
  • Included recursive handling for nested maps and slices via sanitizeValue.

Key Changes by Area

Tool Schema Handling: Added SanitizeTools and sanitizeValue in pkg/functions/functions.go:107 to recursively replace null values in tool.parameters.properties with empty objects.
OpenAI Endpoints: Updated core/http/endpoints/openai/chat.go:625 and inference.go:46 to call SanitizeTools before marshaling tools to JSON.
Testing: Added comprehensive tests in pkg/functions/functions_test.go:85 covering missing parameters, null values, and edge cases.

Files Changed

File Changes Summary
backend/cpp/llama-cpp/grpc-server.cpp No functional changes; file included in PR but unchanged.
core/http/endpoints/openai/chat.go Added SanitizeTools call before JSON serialization of tools.
core/http/endpoints/openai/inference.go Added SanitizeTools call before JSON serialization of tools.
pkg/functions/functions.go Implemented SanitizeTools and sanitizeValue to sanitize tool parameter schemas.
pkg/functions/functions_test.go Added tests for SanitizeTools, including edge cases and immutability checks.

Review Focus Areas

  • Immutability test in pkg/functions/functions_test.go:270-276 may not correctly verify original data is untouched due to how originalNullValue is captured.
  • Behavior of sanitizeValue with deeply nested or mixed-type structures (maps/slices) under real-world tool schemas.

Architecture

Design Decisions: The function uses in-place mutation for simplicity and performance, accepting that the input map may be modified. This avoids copying large schemas unnecessarily. The choice to convert null to {} (empty object) aligns with Jinja’s expectations for object access.
Scalability & Extensibility: The recursive approach supports arbitrary nesting depth, but does not handle circular references (out of scope for current tool schemas).
Risks: The immutability test has a logical flaw (reviewer confidence 92), which may mask unintended mutations. This is a test issue, not a runtime risk, but should be fixed before merge.

Merge Status

MERGEABLE — PR Score 73/100, above threshold (50). All gates passed.

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Nitpicks (Low Priority)

Found 1 low-priority suggestions for code improvement

Click to expand nitpicks

pkg/functions/functions_test.go (lines 270-276)

Robustness Low

The immutability test (lines 270-280) captures originalNullValue before calling SanitizeTools, then checks it is still nil after the call. Because originalNullValue is a plain interface{} copy of the nil value, it will always be nil regardless of whether SanitizeTools mutates the original map. The test should re-read from the original map after the call to actually verify non-mutation: Expect(originalProperties["null_param"]).To(BeNil()).

Code Suggestion or Comments
originalProperties := tools[0].Function.Parameters["properties"].(map[string]interface{})

			sanitized := SanitizeTools(tools)

			// Original should still have nil (re-read from the map to detect mutation)
			Expect(originalProperties["null_param"]).To(BeNil())
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: pkg/functions/functions_test.go
Lines: 270-276
Issue Type: robustness-low
Severity: low

Issue Description:
The immutability test (lines 270-280) captures `originalNullValue` before calling `SanitizeTools`, then checks it is still nil after the call. Because `originalNullValue` is a plain `interface{}` copy of the nil value, it will always be nil regardless of whether `SanitizeTools` mutates the original map. The test should re-read from the original map after the call to actually verify non-mutation: `Expect(originalProperties["null_param"]).To(BeNil())`.

Current Code:
			originalProperties := tools[0].Function.Parameters["properties"].(map[string]interface{})
			originalNullValue := originalProperties["null_param"]

			sanitized := SanitizeTools(tools)

			// Original should still have nil
			Expect(originalNullValue).To(BeNil())

---

### 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

---



Like Dislike

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Security Scan Summary

Metric Value
Vulnerabilities Critical: 0
Overall Risk Clean
Files Scanned 5

No critical security issues detected

Scan completed in 25.2s

Security scan powered by Codity.ai

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Dependency vulnerability scanning

Metric Value
Vulnerabilities Found 4
Scanner pip-audit
View vulnerability details (4 items)

1. pip 24.0

CVE: GHSA-4xh5-x5gv-qwph
Fixed in: 25.3

When extracting a tar archive pip may not check symbolic links point into the extraction directory if the tarfile module doesn't implement PEP 706. Note that upgrading pip to a "fixed" version for thi


2. pip 24.0

CVE: GHSA-6vgw-5pg2-w6jp
Fixed in: 26.0

When pip is installing and extracting a maliciously crafted wheel archive, files may be extracted outside the installation directory. The path traversal is limited to prefixes of the installation dire


3. pip 24.0

CVE: GHSA-58qw-9mgm-455v
Fixed in:

pip handles concatenated tar and ZIP files as ZIP files regardless of filename or whether a file is both a tar and ZIP file. This behavior could result in confusing installation behavior, such as inst


4. pip 24.0

CVE: GHSA-jp4c-xjxw-mgf9
Fixed in: 26.1

pip prior to version 26.1 would run self-update check functionality after installing wheel files which required importing well-known Python modules names. These module imports were intentionally defer

Powered by Codity.ai · Docs

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

License Compliance Scan

Metric Value
Packages Scanned 329
High Risk (Strong Copyleft) 0
Medium Risk (Weak Copyleft) 4
Low Risk (Permissive) 280
Unknown License 45

Weak copyleft licenses found - verify compatibility

Some packages have unknown licenses - manual review required

Medium Risk Licenses - 4 packages

MPL-2.0 (4 packages):

  • github.com/libp2p/go-yamux/v5 5.0.1
  • github.com/hashicorp/golang-lru 1.0.2
  • github.com/hashicorp/golang-lru/v2 2.0.7
  • github.com/shoenig/go-m1cpu 0.1.6
Unknown Licenses - 45 packages
  • github.com/containerd/cgroups 1.1.0
  • github.com/containerd/continuity 0.4.4
  • github.com/containerd/log 0.1.0
  • github.com/containerd/stargz-snapshotter/estargz 0.18.1
  • github.com/creachadair/otp 0.5.0
  • github.com/davidlazar/go-crypto 0.0.0-20200604182044-b73af7476f6c
  • github.com/docker/cli 29.0.3+incompatible
  • github.com/docker/distribution 2.8.3+incompatible
  • github.com/docker/docker 28.5.2+incompatible
  • github.com/docker/docker-credential-helpers 0.9.3
  • github.com/docker/go-connections 0.6.0
  • github.com/docker/go-units 0.5.0
  • github.com/flynn/noise 1.1.0
  • github.com/dsnet/compress 0.0.2-0.20210315054119-f66993602bf5
  • github.com/go-audio/audio 1.0.0
  • github.com/go-audio/riff 1.0.0
  • github.com/go-logr/logr 1.4.3
  • github.com/go-logr/stdr 1.2.2
  • github.com/go-openapi/jsonpointer 0.21.0
  • github.com/go-openapi/jsonreference 0.21.0

...and 25 more

Powered by Codity.ai · Docs

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Code Quality Report — test-org-codity/LocalAI · PR #1

Scanned: 2026-05-16 17:08 UTC | Score: 55/100 | Provider: github

Executive Summary

Severity Count
Critical 0
High 2
Medium 3
Low 1
Top Findings

[CQ-LLM-001] pkg/functions/functions.go:103 (Complexity · HIGH)

Issue: The function 'sanitizeValue' has multiple nested loops and conditionals, increasing cyclomatic complexity.
Suggestion: Consider refactoring 'sanitizeValue' to reduce nesting and improve readability.

func sanitizeValue(value interface{}, path string) interface{} { ... }

[CQ-LLM-003] pkg/functions/functions.go:103 (Error_Handling · HIGH)

Issue: The function 'SanitizeTools' swallows errors during JSON marshaling and unmarshaling without proper handling.
Suggestion: Consider returning an error or logging the error appropriately instead of just returning the original tools.

if err != nil { xlog.Warn("SanitizeTools: failed to marshal tools to JSON", "error", err) return tools }

[CQ-LLM-002] pkg/functions/functions.go:103 (Duplication · MEDIUM)

Issue: The logic for sanitizing maps and slices in 'sanitizeValue' is similar and could be extracted into a separate function to adhere to DRY principles.
Suggestion: Extract common logic for sanitizing maps and slices into a helper function.

if value == nil { ... } switch v := value.(type) { ... }

[CQ-LLM-004] pkg/functions/functions.go:103 (Performance · MEDIUM)

Issue: The 'SanitizeTools' function marshals and unmarshals JSON multiple times, which can be inefficient.
Suggestion: Consider optimizing the sanitization process to avoid unnecessary marshaling and unmarshaling.

toolsJSON, err := json.Marshal(tools) ... var toolsData []map[string]interface{}; json.Unmarshal(toolsJSON, &toolsData)

[CQ-LLM-006] pkg/functions/functions_test.go:82 (Testability · MEDIUM)

Issue: The tests for 'SanitizeTools' rely on hard-coded values, making them less flexible and harder to maintain.
Suggestion: Consider using table-driven tests or parameterized tests to improve test coverage and maintainability.

It("converts null values in parameters.properties to empty objects", func() { ... })

[CQ-LLM-005] pkg/functions/functions.go:103 (Documentation · LOW)

Issue: Missing docstring for the 'sanitizeValue' function, which is a public API.
Suggestion: Add a docstring to 'sanitizeValue' to explain its purpose and usage.

func sanitizeValue(value interface{}, path string) interface{} { ... }

Per-File Breakdown

File Critical High Medium Low Total
pkg/functions/functions.go 0 2 2 1 5
pkg/functions/functions_test.go 0 0 1 0 1

Recommendations

  1. Resolve High severity issues, especially error handling gaps and performance bottlenecks.
  • Run automated tests after applying fixes to verify no regressions.

@DhirenMhatre
Copy link
Copy Markdown
Author

@codity review

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Policy Check Failed

✗ 3/3 policy checks failed:

• Need 2 more approval(s) (0/2) — comment LGTM or approve via review
• Missing ticket reference (expected: JIRA-, ENG-, #*)
• 5 code file(s) changed but no test files added


To merge this PR:

  1. Address the failed checks listed above
  2. Ensure branch protection requires the codity/policy-check status

Configure policies in your dashboard

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

PR Summary

What Changed

  • Added a SanitizeTools function to convert null values in tool parameter schemas to empty objects, preventing Jinja template errors during LLM tool calling.
  • Integrated sanitization into OpenAI chat and inference endpoints before JSON serialization.
  • Included recursive handling for nested maps and slices, with full test coverage for edge cases.

Key Changes by Area

Tool Schema Handling: Added SanitizeTools and sanitizeValue in pkg/functions/functions.go:107 to recursively replace null values in tool.parameters.properties with empty objects.
OpenAI Endpoints: Updated core/http/endpoints/openai/chat.go:625 and inference.go:46 to call SanitizeTools before marshaling tools to JSON.
Testing: Added comprehensive unit tests in pkg/functions/functions_test.go:85 covering missing parameters, null values, and immutability.

Files Changed

File Changes Summary
backend/cpp/llama-cpp/grpc-server.cpp No functional changes; file included in PR but unchanged.
core/http/endpoints/openai/chat.go Added SanitizeTools call before JSON serialization of tools.
core/http/endpoints/openai/inference.go Added SanitizeTools call before JSON serialization of tools.
pkg/functions/functions.go Implemented SanitizeTools and sanitizeValue to sanitize tool parameter schemas.
pkg/functions/functions_test.go Added tests for SanitizeTools covering null handling, nested structures, and immutability.

Review Focus Areas

  • Verify sanitizeValue correctly handles deeply nested structures and preserves non-null values.
  • Confirm SanitizeTools is called consistently before JSON marshaling in all tool-using endpoints.
  • Check test coverage for edge cases like empty objects, slices of maps, and partial nulls.

Architecture

Design Decisions: The function uses in-place mutation on a copy of the input to avoid modifying original schemas, balancing safety and performance. Converting null to {} aligns with Jinja’s expectations for object access.
Scalability & Extensibility: The recursive approach supports arbitrary nesting depth, but does not handle circular references (out of scope).
Risks: Mutating a copy avoids side effects, but the deep copy could impact performance with very large tool schemas. This is acceptable given current usage patterns.

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Security Scan Summary

Metric Value
Vulnerabilities Critical: 0
Overall Risk Clean
Files Scanned 5

No critical security issues detected

Scan completed in 24.2s

Security scan powered by Codity.ai

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Dependency vulnerability scanning

Metric Value
Vulnerabilities Found 4
Scanner pip-audit
View vulnerability details (4 items)

1. pip 24.0

CVE: GHSA-4xh5-x5gv-qwph
Fixed in: 25.3

When extracting a tar archive pip may not check symbolic links point into the extraction directory if the tarfile module doesn't implement PEP 706. Note that upgrading pip to a "fixed" version for thi


2. pip 24.0

CVE: GHSA-6vgw-5pg2-w6jp
Fixed in: 26.0

When pip is installing and extracting a maliciously crafted wheel archive, files may be extracted outside the installation directory. The path traversal is limited to prefixes of the installation dire


3. pip 24.0

CVE: GHSA-58qw-9mgm-455v
Fixed in:

pip handles concatenated tar and ZIP files as ZIP files regardless of filename or whether a file is both a tar and ZIP file. This behavior could result in confusing installation behavior, such as inst


4. pip 24.0

CVE: GHSA-jp4c-xjxw-mgf9
Fixed in: 26.1

pip prior to version 26.1 would run self-update check functionality after installing wheel files which required importing well-known Python modules names. These module imports were intentionally defer

Powered by Codity.ai · Docs

@DhirenMhatre
Copy link
Copy Markdown
Author

@codity review

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Policy Check Failed

✗ 3/3 policy checks failed:

• Need 2 more approval(s) (0/2) — comment LGTM or approve via review
• Missing ticket reference (expected: JIRA-, ENG-, #*)
• 5 code file(s) changed but no test files added


To merge this PR:

  1. Address the failed checks listed above
  2. Ensure branch protection requires the codity/policy-check status

Configure policies in your dashboard

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

PR Summary

What Changed

  • Added SanitizeTools() and sanitizeValue() to recursively replace null values in tool parameter schemas with empty objects.
  • Tools are now sanitized before JSON marshaling in both chat and inference endpoints to prevent Jinja template errors.
  • Added test coverage for edge cases including empty inputs, nested nulls, and immutability.

Key Changes by Area

Tool Schema Handling: Added sanitization logic to convert null parameter values to empty objects, avoiding template rendering failures.
API Endpoints: Integrated sanitization into chat.go and inference.go before JSON serialization.
Testing: Added unit tests for SanitizeTools() covering malformed and valid tool definitions.

Files Changed

File Changes Summary
backend/cpp/llama-cpp/grpc-server.cpp No functional changes; file included in PR but unchanged
core/http/endpoints/openai/chat.go Added call to SanitizeTools() before marshaling tools at line 625
core/http/endpoints/openai/inference.go Added call to SanitizeTools() before marshaling tools at line 46
pkg/functions/functions.go Added SanitizeTools() and sanitizeValue() functions
pkg/functions/functions_test.go Added tests for SanitizeTools() including edge cases

Review Focus Areas

  • Check that SanitizeTools() preserves all non-null values correctly during recursive traversal
  • Verify the test at pkg/functions/functions_test.go:173 handles nil vs empty map behavior after JSON round-trip

Architecture

Design Decisions: Sanitization runs before JSON marshaling to ensure downstream Jinja templates receive valid schemas. Using JSON round-trip for deep copying is intentional but introduces a dependency on struct tags.
Scalability & Extensibility: The recursive approach handles arbitrary nesting depth, but performance may degrade with very large tool schemas (not currently optimized).
Risks: The test assertion at functions_test.go:173 may fail if Parameters unmarshals to an empty map instead of nil. This is a test issue, not a runtime bug, but should be fixed before merge.

Merge Status

MERGEABLE — PR Score 68/100, above threshold (50). All gates passed.

toolsBytes, err := json.Marshal(input.Tools)
// Sanitize tools to remove null values from parameters.properties
sanitizedTools := functions.SanitizeTools(input.Tools)
toolsBytes, err := json.Marshal(sanitizedTools)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Robustness Medium

The error from json.Marshal(sanitizedTools) is silently ignored when err != nil, resulting in toolsJSON remaining an empty string and tools being silently dropped; the error should be logged.

Suggested fix
		toolsBytes, err := json.Marshal(sanitizedTools)
		if err != nil {
			log.Error("Failed to marshal sanitized tools", "error", err)
		} else {
			toolsJSON = string(toolsBytes)
		}
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/openai/chat.go
Lines: 627-627
Issue Type: robustness-medium
Severity: medium

Issue Description:
The error from `json.Marshal(sanitizedTools)` is silently ignored when `err != nil`, resulting in `toolsJSON` remaining an empty string and tools being silently dropped; the error should be logged.

Current Code:
		toolsBytes, err := json.Marshal(sanitizedTools)
		if err == nil {
			toolsJSON = string(toolsBytes)
		}

---

### 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

---


Like Dislike Create Issue Jira

Comment on lines +270 to +276
originalProperties := tools[0].Function.Parameters["properties"].(map[string]interface{})
originalNullValue := originalProperties["null_param"]

sanitized := SanitizeTools(tools)

// Original should still have nil
Expect(originalNullValue).To(BeNil())
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional Medium

The immutability test captures originalNullValue before calling SanitizeTools, so it will always be nil regardless of whether SanitizeTools mutates the original; the test does not actually verify that the original map entry was not modified in-place. To properly test immutability, re-read originalProperties["null_param"] after the call.

Suggested fix
			originalProperties := tools[0].Function.Parameters["properties"].(map[string]interface{})

			sanitized := SanitizeTools(tools)

			// Original should still have nil (re-read after the call to detect in-place mutation)
			Expect(originalProperties["null_param"]).To(BeNil())
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: pkg/functions/functions_test.go
Lines: 270-276
Issue Type: functional-medium
Severity: medium

Issue Description:
The immutability test captures `originalNullValue` before calling `SanitizeTools`, so it will always be nil regardless of whether `SanitizeTools` mutates the original; the test does not actually verify that the original map entry was not modified in-place. To properly test immutability, re-read `originalProperties["null_param"]` after the call.

Current Code:
			originalProperties := tools[0].Function.Parameters["properties"].(map[string]interface{})
			originalNullValue := originalProperties["null_param"]

			sanitized := SanitizeTools(tools)

			// Original should still have nil
			Expect(originalNullValue).To(BeNil())

---

### 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

---


Like Dislike Create Issue Jira

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Nitpicks (Low Priority)

Found 1 low-priority suggestions for code improvement

Click to expand nitpicks

pkg/functions/functions_test.go (line 173)

Robustness Low

The test at line 173 asserts Parameters is BeNil(), but SanitizeTools round-trips through JSON marshal/unmarshal; an empty/nil Parameters field with json:"parameters" (no omitempty) may unmarshal as an empty map rather than nil, causing a false test failure or masking a real bug. The field tag should include omitempty, or the assertion should use BeEmpty() / Or(BeNil(), BeEmpty()).

Also reported at: pkg/functions/functions_test.go L252

Code Suggestion or Comments
Expect(sanitized[0].Function.Parameters).To(Or(BeNil(), BeEmpty()))
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: pkg/functions/functions_test.go
Lines: 173-173
Issue Type: robustness-low
Severity: low

Issue Description:
The test at line 173 asserts `Parameters` is `BeNil()`, but `SanitizeTools` round-trips through JSON marshal/unmarshal; an empty/nil `Parameters` field with `json:"parameters"` (no `omitempty`) may unmarshal as an empty map rather than nil, causing a false test failure or masking a real bug. The field tag should include `omitempty`, or the assertion should use `BeEmpty()` / `Or(BeNil(), BeEmpty())`.

_Also reported at: `pkg/functions/functions_test.go` L252_

Current Code:
			Expect(sanitized[0].Function.Parameters).To(BeNil())

---

### 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

---



Like Dislike

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Security Scan Summary

Metric Value
Vulnerabilities Critical: 0
Overall Risk Clean
Files Scanned 5

No critical security issues detected

Scan completed in 19.6s

Security scan powered by Codity.ai

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Dependency vulnerability scanning

Metric Value
Vulnerabilities Found 4
Scanner pip-audit
View vulnerability details (4 items)

1. pip 24.0

CVE: GHSA-4xh5-x5gv-qwph
Fixed in: 25.3

When extracting a tar archive pip may not check symbolic links point into the extraction directory if the tarfile module doesn't implement PEP 706. Note that upgrading pip to a "fixed" version for thi


2. pip 24.0

CVE: GHSA-6vgw-5pg2-w6jp
Fixed in: 26.0

When pip is installing and extracting a maliciously crafted wheel archive, files may be extracted outside the installation directory. The path traversal is limited to prefixes of the installation dire


3. pip 24.0

CVE: GHSA-58qw-9mgm-455v
Fixed in:

pip handles concatenated tar and ZIP files as ZIP files regardless of filename or whether a file is both a tar and ZIP file. This behavior could result in confusing installation behavior, such as inst


4. pip 24.0

CVE: GHSA-jp4c-xjxw-mgf9
Fixed in: 26.1

pip prior to version 26.1 would run self-update check functionality after installing wheel files which required importing well-known Python modules names. These module imports were intentionally defer

Powered by Codity.ai · Docs

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

License Compliance Scan

Metric Value
Packages Scanned 329
High Risk (Strong Copyleft) 0
Medium Risk (Weak Copyleft) 4
Low Risk (Permissive) 271
Unknown License 54

Weak copyleft licenses found - verify compatibility

Some packages have unknown licenses - manual review required

Medium Risk Licenses - 4 packages

MPL-2.0 (4 packages):

  • github.com/libp2p/go-yamux/v5 5.0.1
  • github.com/hashicorp/golang-lru 1.0.2
  • github.com/hashicorp/golang-lru/v2 2.0.7
  • github.com/shoenig/go-m1cpu 0.1.6
Unknown Licenses - 54 packages
  • github.com/docker/go-connections 0.6.0
  • github.com/docker/go-units 0.5.0
  • github.com/dsnet/compress 0.0.2-0.20210315054119-f66993602bf5
  • github.com/flynn/noise 1.1.0
  • github.com/go-audio/audio 1.0.0
  • github.com/go-audio/riff 1.0.0
  • github.com/go-logr/logr 1.4.3
  • github.com/go-logr/stdr 1.2.2
  • github.com/go-openapi/jsonpointer 0.21.0
  • github.com/go-openapi/spec 0.21.0
  • github.com/go-openapi/jsonreference 0.21.0
  • github.com/go-openapi/swag 0.23.0
  • github.com/gogo/protobuf 1.3.2
  • github.com/golang/groupcache 0.0.0-20210331224755-41bb18bfe9da
  • github.com/golang/snappy 0.0.4
  • github.com/google/btree 1.1.3
  • github.com/google/go-cmp 0.7.0
  • github.com/google/gopacket 1.1.19
  • github.com/google/pprof 0.0.0-20250630185457-6e76a2b096b5
  • github.com/gorilla/css 1.0.1

...and 34 more

Powered by Codity.ai · Docs

@codity-dm
Copy link
Copy Markdown

codity-dm Bot commented May 16, 2026

Code Quality Report — test-org-codity/LocalAI · PR #1

Scanned: 2026-05-16 17:41 UTC | Score: 54/100 | Provider: github

Executive Summary

Severity Count
Critical 0
High 2
Medium 3
Low 2
Top Findings

[CQ-LLM-001] pkg/functions/functions.go:103 (Complexity · HIGH)

Issue: The function sanitizeValue has multiple nested loops and conditionals, increasing cyclomatic complexity.
Suggestion: Consider refactoring the function to reduce nesting and improve readability.

for key, val := range v { ... }

[CQ-LLM-003] pkg/functions/functions.go:112 (Error_Handling · HIGH)

Issue: The function SanitizeTools swallows errors during JSON marshaling and unmarshaling without proper handling.
Suggestion: Log the errors or handle them appropriately to avoid silent failures.

if err != nil { return tools }

[CQ-LLM-002] pkg/functions/functions.go:103 (Duplication · MEDIUM)

Issue: The logic for sanitizing maps and slices is similar and could be abstracted to reduce duplication.
Suggestion: Create a helper function to handle the common sanitization logic for both maps and slices.

case map[string]interface{}: ... case []interface{}: ...

[CQ-LLM-004] pkg/functions/functions.go:112 (Performance · MEDIUM)

Issue: The use of JSON marshaling and unmarshaling for sanitization may introduce performance overhead.
Suggestion: Consider implementing a direct sanitization approach without converting to JSON.

toolsJSON, err := json.Marshal(tools)

[CQ-LLM-006] pkg/functions/functions_test.go:82 (Testability · MEDIUM)

Issue: The SanitizeTools function relies on global state (Tools type) which can make testing more difficult.
Suggestion: Consider using dependency injection or passing dependencies explicitly to improve testability.

tools := Tools{ ... }

[CQ-LLM-005] pkg/functions/functions.go:103 (Documentation · LOW)

Issue: Missing docstring for the sanitizeValue function.
Suggestion: Add a docstring to explain the purpose and usage of the sanitizeValue function.

func sanitizeValue(value interface{}, path string) interface{} { ... }

[CQ-LLM-007] pkg/functions/functions.go:103 (Maintainability · LOW)

Issue: The use of magic numbers or hard-coded values in the sanitization logic can reduce maintainability.
Suggestion: Define constants for any hard-coded values to improve readability and maintainability.

len(v)

Per-File Breakdown

File Critical High Medium Low Total
pkg/functions/functions.go 0 2 2 2 6
pkg/functions/functions_test.go 0 0 1 0 1

Recommendations

  1. Resolve High severity issues, especially error handling gaps and performance bottlenecks.
  • Run automated tests after applying fixes to verify no regressions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants