Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions cmd/cli/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/docker/model-runner/cmd/cli/commands/completion"
"github.com/docker/model-runner/cmd/cli/desktop"
"github.com/docker/model-runner/cmd/cli/readline"
"github.com/docker/model-runner/cmd/cli/tools"
"github.com/docker/model-runner/pkg/inference"
"github.com/docker/model-runner/pkg/inference/scheduling"
"github.com/fatih/color"
Expand All @@ -24,6 +25,15 @@ import (
"golang.org/x/term"
)

// defaultTools returns the tools enabled by default for interactive sessions.
// Web search can be disabled by setting DOCKER_MODEL_NO_WEBSEARCH=1.
func defaultTools() []desktop.ClientTool {
if os.Getenv("DOCKER_MODEL_NO_WEBSEARCH") != "" {
return nil
}
return []desktop.ClientTool{&tools.WebSearchTool{}}
}

// readMultilineInput reads input from stdin, supporting both single-line and multiline input.
// For multiline input, it detects triple-quoted strings and shows continuation prompts.
func readMultilineInput(cmd *cobra.Command, scanner *bufio.Scanner) (string, error) {
Expand Down Expand Up @@ -632,11 +642,13 @@ func chatWithMarkdownContext(ctx context.Context, cmd *cobra.Command, client *de
// This reflects exactly what the model receives.
processedUserMessage = buildUserMessage(prompt, imageURLs)

activeTools := defaultTools()

if !useMarkdown {
// Simple case: just stream as plain text
assistantResponse, err = client.ChatWithMessagesContext(ctx, model, conversationHistory, prompt, imageURLs, func(content string) {
cmd.Print(content)
}, false)
}, false, activeTools...)
return assistantResponse, processedUserMessage, err
}

Expand All @@ -655,7 +667,7 @@ func chatWithMarkdownContext(ctx context.Context, cmd *cobra.Command, client *de
} else if rendered != "" {
cmd.Print(rendered)
}
}, true)
}, true, activeTools...)
if err != nil {
return assistantResponse, processedUserMessage, err
}
Expand Down
46 changes: 39 additions & 7 deletions cmd/cli/desktop/api.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,37 @@
package desktop

// Tool represents an OpenAI function tool definition.
type Tool struct {
Type string `json:"type"`
Function ToolFunction `json:"function"`
}

// ToolFunction holds the schema for a tool.
type ToolFunction struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Parameters any `json:"parameters,omitempty"`
}

// ToolCall represents a tool call in a message or streaming delta.
type ToolCall struct {
ID string `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Index int `json:"index"`
Function ToolCallFunction `json:"function"`
}

// ToolCallFunction holds the name and accumulated arguments for a tool call.
type ToolCallFunction struct {
Name string `json:"name,omitempty"`
Arguments string `json:"arguments,omitempty"`
}

type OpenAIChatMessage struct {
Role string `json:"role"`
Content interface{} `json:"content"` // Can be string or []ContentPart for multimodal
Role string `json:"role"`
Content any `json:"content,omitempty"` // Can be string or []ContentPart for multimodal
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
}

// ContentPart represents a part of multimodal content (text or image)
Expand All @@ -21,6 +50,7 @@ type OpenAIChatRequest struct {
Model string `json:"model"`
Messages []OpenAIChatMessage `json:"messages"`
Stream bool `json:"stream"`
Tools []Tool `json:"tools,omitempty"`
}

type OpenAIChatResponse struct {
Expand All @@ -30,13 +60,15 @@ type OpenAIChatResponse struct {
Model string `json:"model"`
Choices []struct {
Delta struct {
Content string `json:"content"`
Role string `json:"role,omitempty"`
ReasoningContent string `json:"reasoning_content,omitempty"`
Content string `json:"content"`
Role string `json:"role,omitempty"`
ReasoningContent string `json:"reasoning_content,omitempty"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
} `json:"delta"`
Message struct {
Content string `json:"content"`
Role string `json:"role,omitempty"`
Content string `json:"content"`
Role string `json:"role,omitempty"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
} `json:"message"`
Index int `json:"index"`
FinishReason string `json:"finish_reason"`
Expand Down
Loading
Loading