Skip to content

fix: bonjour agent calls now run tools on the server#767

Merged
ritave merged 9 commits intomainfrom
ritave/bonjour-on-server
Apr 7, 2026
Merged

fix: bonjour agent calls now run tools on the server#767
ritave merged 9 commits intomainfrom
ritave/bonjour-on-server

Conversation

@ritave
Copy link
Copy Markdown
Contributor

@ritave ritave commented Apr 2, 2026

Summary

Previously the Bonjour agents would run tools on the client instead of server. This fixes that.

Changes

  • Behavior change
  • UI change (screenshots below)
  • Refactor / chore
  • Tests
  • Docs

Test Plan

Set up bonjour agent, connect to it, try to run one of the tools

Screenshots

image

Checklist

  • I have read CONTRIBUTING.md
  • I added/updated tests where reasonable
  • I updated docs/README as needed
  • I verified build on macOS with Xcode 16.4+

@ritave ritave force-pushed the ritave/bonjour-on-server branch from 49ca517 to 7e7b1e0 Compare April 3, 2026 13:48
@ritave ritave marked this pull request as ready for review April 3, 2026 13:59
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR changes Bonjour-discovered “Osaurus agents” so chat/tool execution happens on the remote server (via new /agents/{id}/run), with the client receiving streamed SSE deltas and tool-call display hints.

Changes:

  • Add server endpoints to fetch a single agent (GET /agents/{id}) and run an agent loop server-side (POST /agents/{id}/run).
  • Introduce a new remote provider type (.osaurus) that targets /agents/{id}/run and fetches models appropriately.
  • Update the chat UI to connect to discovered agents using the new provider type and to render server-side tool calls/results via streaming sentinels.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
Packages/OsaurusCore/Views/Settings/ServerView.swift Documents the new agent endpoints in the in-app API endpoint list.
Packages/OsaurusCore/Views/Chat/ChatView.swift Switches Bonjour connections to .osaurus providers and adds handling for server-side tool “done” hints in streaming UI.
Packages/OsaurusCore/Services/Provider/RemoteProviderService.swift Adds .osaurus routing: /agents/{id}/run URL building and model fetching logic.
Packages/OsaurusCore/Services/Inference/ModelService.swift Extends streaming tool hint protocol with a “done” payload for tool result display.
Packages/OsaurusCore/Networking/HTTPHandler.swift Implements GET /agents/{id} and POST /agents/{id}/run (server-side inference + tool loop + SSE streaming).
Packages/OsaurusCore/Models/Configuration/RemoteProviderConfiguration.swift Adds .osaurus provider type and persists remoteAgentId in provider configuration.
Packages/OsaurusCore/Managers/RemoteProviderManager.swift Treats .osaurus like OpenAI-style auth header (Authorization: Bearer ...) when testing connections.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


// Extract agent ID: /agents/{id}
let components = path.split(separator: "/")
guard components.count >= 2, let agentId = UUID(uuidString: String(components[1])) else {
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

handleGetAgentEndpoint extracts the agent UUID from path.split("/") but only checks components.count >= 2. This will treat paths like /agents/{id}/run (or any extra suffix segments) as if they were /agents/{id}. If the intent is to serve only GET /agents/{id}, tighten the validation to require exactly two path components ("agents", "{id}") and return 404/400 otherwise.

Suggested change
guard components.count >= 2, let agentId = UUID(uuidString: String(components[1])) else {
guard
components.count == 2,
components[0] == "agents",
let agentId = UUID(uuidString: String(components[1]))
else {

Copilot uses AI. Check for mistakes.
Comment on lines +1683 to +1691
guard let req = try? JSONDecoder().decode(ChatCompletionRequest.self, from: data) else {
sendResponse(
context: context,
version: head.version,
status: .badRequest,
headers: [("Content-Type", "text/plain; charset=utf-8")],
body: "Invalid request format"
)
return
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

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

The /agents/{id}/run handler decodes a ChatCompletionRequest (which uses max_tokens), but the client-side RemoteProviderService sends RemoteChatRequest with max_completion_tokens. Because max_completion_tokens is ignored by ChatCompletionRequest, the server will silently drop the caller’s max-token setting (and any other fields not represented on ChatCompletionRequest). Consider adding backward/forward-compatible decoding (accept both max_tokens and max_completion_tokens) or decoding the exact request type you expect for this endpoint.

Copilot uses AI. Check for mistakes.
ritave added 9 commits April 7, 2026 12:10
1. Chat can now select model again when calling remote.
2. Fix remote chat not working with agent being default model
3. Remote chat is a new session
1. Sandbox can be executed if enabled on the server
2. Tool call should be visible on the client
@ritave ritave force-pushed the ritave/bonjour-on-server branch from 1aeda36 to e51d842 Compare April 7, 2026 12:46
@ritave ritave merged commit 75dc0e9 into main Apr 7, 2026
5 checks passed
@ritave ritave deleted the ritave/bonjour-on-server branch April 7, 2026 13:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants