Skip to content

fix(compat): restore namespace on function_call items after flattening#5065

Open
tcx4c70 wants to merge 1 commit into
maximhq:devfrom
tcx4c70:fix/namespace-tools
Open

fix(compat): restore namespace on function_call items after flattening#5065
tcx4c70 wants to merge 1 commit into
maximhq:devfrom
tcx4c70:fix/namespace-tools

Conversation

@tcx4c70

@tcx4c70 tcx4c70 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

flattenNamespaceTools expands namespace-scoped Responses tools into a flat list for providers that don't support the "namespace" tool type, but it dropped the namespace name. Providers then return function_call items with only a name and no namespace, so namespace-tool clients (e.g. Codex) that match tools by (namespace, name) fail with "unsupported tool ".

Changes

Record a tool-name -> namespace mapping while flattening (stored per-request on BifrostContext), and re-attach the namespace to function_call items in PostLLMHook for both non-streaming (ResponsesResponse.Output) and streaming (ResponsesStreamResponse.Item and Response.Output) responses. A provider-supplied namespace is never overwritten, and OpenAI/Azure-OpenAI paths are unaffected since they skip flattening.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

Describe the steps to validate this change. Include commands and expected outcomes.

# Core/Transports
go version
go test ./...

# UI
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build

If adding new configs or environment variables, document them here.

Screenshots/Recordings

If UI changes, add before/after screenshots or short clips.

Breaking changes

  • Yes
  • No

If yes, describe impact and migration instructions.

Related issues

Link related issues and discussions. Example: Closes #123

Security considerations

Note any security implications (auth, secrets, PII, sandboxing, etc.).

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

flattenNamespaceTools expands namespace-scoped Responses tools into a flat
list for providers that don't support the "namespace" tool type, but it
dropped the namespace name. Providers then return function_call items with
only a name and no namespace, so namespace-tool clients (e.g. Codex) that
match tools by (namespace, name) fail with "unsupported tool <name>".

Record a tool-name -> namespace mapping while flattening (stored per-request
on BifrostContext), and re-attach the namespace to function_call items in
PostLLMHook for both non-streaming (ResponsesResponse.Output) and streaming
(ResponsesStreamResponse.Item and Response.Output) responses. A
provider-supplied namespace is never overwritten, and OpenAI/Azure-OpenAI
paths are unaffected since they skip flattening.
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Improved handling of namespaced tools so tool names can be flattened for providers that need it and restored in responses afterward.
    • Namespaces are now preserved across both standard and streaming response flows.
  • Bug Fixes

    • Prevents namespace information from being lost on returned function-call items.
    • Keeps any namespace already provided by the model unchanged.
  • Tests

    • Added coverage for tool flattening, namespace restoration, streaming responses, and full request/response round trips.

Walkthrough

This PR adds namespace-tool flattening with mapping capture in flattenNamespaceTools, threads a BifrostContext through applyParameterConversion to store that mapping, and introduces response-side namespace restoration (restoreNamespaceOnResponse and helpers) invoked from PostLLMHook. A new test file validates flattening, context storage, and streaming/non-streaming restoration.

Changes

Namespace flattening and restoration

Layer / File(s) Summary
Namespace tool flattening and mapping
plugins/compat/conversion.go
flattenNamespaceTools now returns a map[string]string mapping expanded tool names to originating namespaces, building the flattened tools list alongside the mapping, with early returns for skipped providers/models.
Context-aware conversion and response restoration
plugins/compat/conversion.go
applyParameterConversion takes a *schemas.BifrostContext, stores the namespace mapping under a private context key, and new restoreNamespaceOnResponse/restoreNamespaceOnMessages/restoreNamespaceOnMessage functions traverse non-streaming and streaming outputs to reattach missing namespaces without overwriting existing ones.
Hook wiring
plugins/compat/main.go
PreLLMHook passes ctx into applyParameterConversion; PostLLMHook reads the namespace map from context and calls restoreNamespaceOnResponse before returning the response.
Namespace flattening/restoration tests
plugins/compat/namespacetools_test.go
New test file with helpers and tests covering flattening for Anthropic/OpenAI, context storage, streaming/non-streaming restoration, non-overwrite behavior, unknown tools, and a full plugin round-trip.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PreLLMHook
  participant applyParameterConversion
  participant flattenNamespaceTools
  participant BifrostContext
  participant PostLLMHook
  participant restoreNamespaceOnResponse

  PreLLMHook->>applyParameterConversion: applyParameterConversion(ctx, req)
  applyParameterConversion->>flattenNamespaceTools: flattenNamespaceTools(req)
  flattenNamespaceTools-->>applyParameterConversion: namespaceByTool map
  applyParameterConversion->>BifrostContext: store namespaceToolMap
  PostLLMHook->>BifrostContext: read namespaceToolMap
  PostLLMHook->>restoreNamespaceOnResponse: restoreNamespaceOnResponse(result, namespaceByTool)
  restoreNamespaceOnResponse-->>PostLLMHook: response with restored Namespace
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR does not address #123 Files API support; it changes namespace restoration in compat instead of /v1/files provider support. Implement provider Files API support for OpenAI/Anthropic, including the /v1/files workflow, and add tests covering upload-related paths.
Out of Scope Changes check ⚠️ Warning These changes are outside the linked issue scope because they implement namespace restoration rather than Files API support. Align the PR with #123 by adding or replacing this work with Files API functionality and corresponding provider/test coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: restoring namespaces on function_call items after flattening.
Description check ✅ Passed The description is mostly complete and follows the template, though related issues, security, and test details are still generic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 4/5

The namespace restore path needs a fix for duplicate inner tool names before merging.

  • The new context handoff and streaming restore paths are guarded for the main nil and event-shape cases.
  • The restore map drops the namespace dimension when two namespaces share a function name.
  • Clients can receive a function call for the wrong namespace and run or reject the wrong tool.

plugins/compat/conversion.go

Important Files Changed

Filename Overview
plugins/compat/conversion.go Adds namespace restoration helpers and changes flattening to return a tool-name map, but duplicate inner names across namespaces can collide.
plugins/compat/main.go Passes context into parameter conversion and restores namespaces in the post hook when a mapping is present.
plugins/compat/namespacetools_test.go Adds coverage for single-namespace flattening and restoration across non-streaming and streaming responses.

Reviews (1): Last reviewed commit: "fix(compat): restore namespace on functi..." | Re-trigger Greptile

if namespaceByTool == nil {
namespaceByTool = make(map[string]string)
}
namespaceByTool[*inner.Name] = namespace

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Namespace Key Collisions

When two namespace tools expose the same inner function name, this map keeps only the last namespace for that name. A non-OpenAI provider then returns function_call.name without the namespace, so restoring by name alone can label ns_a.search as ns_b.search, causing the client to execute the wrong namespaced tool or reject the call.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
plugins/compat/namespacetools_test.go (1)

74-91: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a test for the Azure skip path.

flattenNamespaceTools has a distinct Azure branch (req.Provider == schemas.Azure && !schemas.IsAnthropicModel(req.Model)) that is not exercised. The OpenAI skip is tested, but the Azure condition involves an additional IsAnthropicModel check — both the skip case (Azure + non-Anthropic model) and the proceed case (Azure + Anthropic model) are untested.

🧪 Suggested Azure skip test
 func TestFlattenNamespaceTools_SkipsOpenAI(t *testing.T) {
 	// ... existing code ...
 }

+func TestFlattenNamespaceTools_SkipsAzureNonAnthropic(t *testing.T) {
+	req := &schemas.BifrostResponsesRequest{
+		Provider: schemas.Azure,
+		Model:    "gpt-5.4",
+		Params: &schemas.ResponsesParameters{
+			Tools: []schemas.ResponsesTool{namespaceTool("mcp__node_repl__", "js")},
+		},
+	}
+
+	nsMap := flattenNamespaceTools(req)
+
+	if nsMap != nil {
+		t.Errorf("expected nil map for Azure non-Anthropic, got %v", nsMap)
+	}
+	if len(req.Params.Tools) != 1 || req.Params.Tools[0].Type != schemas.ResponsesToolTypeNamespace {
+		t.Errorf("Azure non-Anthropic namespace tool should be left intact, got %+v", req.Params.Tools)
+	}
+}
+
+func TestFlattenNamespaceTools_ProceedsForAzureAnthropicModel(t *testing.T) {
+	req := &schemas.BifrostResponsesRequest{
+		Provider: schemas.Azure,
+		Model:    "claude-sonnet-4",
+		Params: &schemas.ResponsesParameters{
+			Tools: []schemas.ResponsesTool{namespaceTool("mcp__node_repl__", "js")},
+		},
+	}
+
+	nsMap := flattenNamespaceTools(req)
+
+	if nsMap == nil || nsMap["js"] != "mcp__node_repl__" {
+		t.Fatalf("expected namespace map for Azure Anthropic model, got %v", nsMap)
+	}
+	if got := len(req.Params.Tools); got != 1 || req.Params.Tools[0].Type != schemas.ResponsesToolTypeFunction {
+		t.Fatalf("expected flattened function tool for Azure Anthropic, got %+v", req.Params.Tools)
+	}
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/compat/namespacetools_test.go` around lines 74 - 91, Add coverage for
the Azure-specific branch in flattenNamespaceTools. Create tests that exercise
the req.Provider == schemas.Azure path with a non-Anthropic model to confirm
namespace tools are skipped and left unchanged, and another case with an
Anthropic model to confirm the function proceeds as expected. Use
flattenNamespaceTools, schemas.Azure, and schemas.IsAnthropicModel in the new
test names/fixtures so the Azure behavior is explicitly covered alongside the
existing OpenAI test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@plugins/compat/namespacetools_test.go`:
- Around line 74-91: Add coverage for the Azure-specific branch in
flattenNamespaceTools. Create tests that exercise the req.Provider ==
schemas.Azure path with a non-Anthropic model to confirm namespace tools are
skipped and left unchanged, and another case with an Anthropic model to confirm
the function proceeds as expected. Use flattenNamespaceTools, schemas.Azure, and
schemas.IsAnthropicModel in the new test names/fixtures so the Azure behavior is
explicitly covered alongside the existing OpenAI test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4ef5d1d6-1d33-42b0-ae7f-302186fd098e

📥 Commits

Reviewing files that changed from the base of the PR and between 838d5a2 and 1073e07.

📒 Files selected for processing (3)
  • plugins/compat/conversion.go
  • plugins/compat/main.go
  • plugins/compat/namespacetools_test.go

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.

Files API Support

1 participant