From f58af906b0d36260985ce55a85dfbeb7262beadf Mon Sep 17 00:00:00 2001
From: emilyjiji <92887308+emilyjiji@users.noreply.github.com>
Date: Wed, 13 Aug 2025 20:02:47 -0400
Subject: [PATCH 1/3] Add Ollama provider (--use-ollama) + config keys; disable
tools when unsupported
---
src/common/Configuration/KnownSettings.cs | 97 +++++++++++--------
src/cycod/ChatClient/ChatClientFactory.cs | 50 +++++++++-
src/cycod/ChatClient/FunctionCallingChat.cs | 18 ++--
.../CommandLine/CycoDevCommandLineOptions.cs | 32 ++++--
4 files changed, 142 insertions(+), 55 deletions(-)
diff --git a/src/common/Configuration/KnownSettings.cs b/src/common/Configuration/KnownSettings.cs
index d5e2c9249..a050f0cba 100644
--- a/src/common/Configuration/KnownSettings.cs
+++ b/src/common/Configuration/KnownSettings.cs
@@ -13,13 +13,13 @@ public static class KnownSettings
// Anthropic settings
public const string AnthropicApiKey = "Anthropic.ApiKey";
public const string AnthropicModelName = "Anthropic.ModelName";
-
+
// AWS Bedrock settings
public const string AWSBedrockAccessKey = "AWS.Bedrock.AccessKey";
public const string AWSBedrockSecretKey = "AWS.Bedrock.SecretKey";
public const string AWSBedrockRegion = "AWS.Bedrock.Region";
public const string AWSBedrockModelId = "AWS.Bedrock.ModelId";
-
+
// Google Gemini settings
public const string GoogleGeminiApiKey = "Google.Gemini.ApiKey";
public const string GoogleGeminiModelId = "Google.Gemini.ModelId";
@@ -33,15 +33,19 @@ public static class KnownSettings
public const string AzureOpenAIApiKey = "Azure.OpenAI.ApiKey";
public const string AzureOpenAIEndpoint = "Azure.OpenAI.Endpoint";
public const string AzureOpenAIChatDeployment = "Azure.OpenAI.ChatDeployment";
-
+
// OpenAI settings
public const string OpenAIApiKey = "OpenAI.ApiKey";
public const string OpenAIEndpoint = "OpenAI.Endpoint";
public const string OpenAIChatModelName = "OpenAI.ChatModelName";
-
+
+ //Ollama settings
+ public const string OllamaBaseUrl = "Ollama.BaseUrl";
+ public const string OllamaModelId = "Ollama.ModelId";
+
// GitHub settings
public const string GitHubToken = "GitHub.Token";
-
+
// Copilot settings
public const string CopilotModelName = "Copilot.ModelName";
public const string CopilotApiEndpoint = "Copilot.ApiEndpoint";
@@ -59,11 +63,11 @@ public static class KnownSettings
public const string AppChatCompletionTimeout = "App.ChatCompletionTimeout";
public const string AppAutoApprove = "App.AutoApprove";
public const string AppAutoDeny = "App.AutoDeny";
-
+
#endregion
-
+
#region Secret Settings
-
+
///
/// Collection of known setting names that should be treated as secrets.
///
@@ -91,11 +95,11 @@ public static class KnownSettings
// GitHub secrets
GitHubToken,
};
-
+
#endregion
-
+
#region Setting Format Mappings
-
+
///
/// Mapping from dot notation to environment variable format.
///
@@ -129,6 +133,10 @@ public static class KnownSettings
{ OpenAIApiKey, "OPENAI_API_KEY" },
{ OpenAIEndpoint, "OPENAI_ENDPOINT" },
{ OpenAIChatModelName, "OPENAI_CHAT_MODEL_NAME" },
+
+ //Ollama mappings
+ { OllamaBaseUrl, "OLLAMA_BASE_URL" },
+ { OllamaModelId, "OLLAMA_MODEL_ID" },
// GitHub mappings
{ GitHubToken, "GITHUB_TOKEN" },
@@ -151,7 +159,7 @@ public static class KnownSettings
{ AppAutoApprove, "CYCOD_AUTO_APPROVE" },
{ AppAutoDeny, "CYCOD_AUTO_DENY" }
};
-
+
///
/// Mapping from dot notation to CLI option format.
///
@@ -185,6 +193,10 @@ public static class KnownSettings
{ OpenAIApiKey, "--openai-api-key" },
{ OpenAIEndpoint, "--openai-endpoint" },
{ OpenAIChatModelName, "--openai-chat-model-name" },
+
+ // Ollama mappings
+ { OllamaBaseUrl, "--ollama-base-url" },
+ { OllamaModelId, "--ollama-model-id" },
// GitHub mappings
{ GitHubToken, "--github-token" },
@@ -206,12 +218,12 @@ public static class KnownSettings
{ AppAutoApprove, "--auto-approve" },
{ AppAutoDeny, "--auto-deny" }
};
-
+
///
/// Mapping from environment variable format to dot notation.
///
private static readonly Dictionary _envVarToDotMap;
-
+
///
/// Mapping from CLI option format to dot notation.
///
@@ -237,7 +249,7 @@ public static class KnownSettings
AnthropicApiKey,
AnthropicModelName
};
-
+
///
/// Collection of settings for AWS Bedrock integration.
///
@@ -248,7 +260,7 @@ public static class KnownSettings
AWSBedrockRegion,
AWSBedrockModelId
};
-
+
///
/// Collection of settings for Google Gemini integration.
///
@@ -257,7 +269,7 @@ public static class KnownSettings
GoogleGeminiApiKey,
GoogleGeminiModelId
};
-
+
///
/// Collection of settings for Grok integration.
///
@@ -277,7 +289,7 @@ public static class KnownSettings
AzureOpenAIEndpoint,
AzureOpenAIChatDeployment
};
-
+
///
/// Collection of settings for OpenAI integration.
///
@@ -287,7 +299,16 @@ public static class KnownSettings
OpenAIEndpoint,
OpenAIChatModelName
};
-
+
+ ///
+ /// Collection of settings for Ollama integration.
+ ///
+ public static readonly HashSet OllamaSettings = new(StringComparer.OrdinalIgnoreCase)
+ {
+ OllamaBaseUrl,
+ OllamaModelId
+ };
+
///
/// Collection of settings for GitHub integration.
///
@@ -295,7 +316,7 @@ public static class KnownSettings
{
GitHubToken
};
-
+
///
/// Collection of settings for Copilot integration.
///
@@ -306,7 +327,7 @@ public static class KnownSettings
CopilotIntegrationId,
CopilotEditorVersion
};
-
+
///
/// Collection of settings for application configuration.
///
@@ -323,16 +344,16 @@ public static class KnownSettings
AppAutoApprove,
AppAutoDeny
};
-
+
#endregion
-
+
static KnownSettings()
{
// Initialize reverse mappings
_envVarToDotMap = _dotToEnvVarMap.ToDictionary(kvp => kvp.Value, kvp => kvp.Key, StringComparer.OrdinalIgnoreCase);
_cliOptionToDotMap = _dotToCLIOptionMap.ToDictionary(kvp => kvp.Value, kvp => kvp.Key, StringComparer.OrdinalIgnoreCase);
}
-
+
///
/// Checks if the given key is a known setting.
///
@@ -345,7 +366,7 @@ public static bool IsKnown(string key)
if (_cliOptionToDotMap.ContainsKey(key)) return true;
return false;
}
-
+
///
/// Checks if the given key is a secret value that should be obfuscated.
///
@@ -356,7 +377,7 @@ public static bool IsSecret(string key)
var dotNotationKey = ToDotNotation(key);
return _secretSettings.Contains(dotNotationKey);
}
-
+
///
/// Gets all known setting names in dot notation format.
///
@@ -376,7 +397,7 @@ public static bool IsMultiValue(string key)
var dotNotationKey = ToDotNotation(key);
return _multiValueSettings.Contains(dotNotationKey, StringComparer.OrdinalIgnoreCase);
}
-
+
///
/// Gets the canonical form of a known setting key.
///
@@ -418,7 +439,7 @@ public static string ToEnvironmentVariable(string key)
// Otherwise, just return it
return key;
}
-
+
///
/// Converts a setting name to CLI option format.
///
@@ -427,20 +448,20 @@ public static string ToEnvironmentVariable(string key)
public static string ToCLIOption(string key)
{
if (IsCLIOptionFormat(key)) return key;
-
+
// Try to map to a CLI option
var dotNotationKey = ToDotNotation(key);
if (_dotToCLIOptionMap.TryGetValue(dotNotationKey, out string? cliOption))
{
return cliOption;
}
-
+
// Otherwise, use a general algorithm
var parts = dotNotationKey.Split('.');
var kebabParts = parts.Select(p => ToKebabCase(p));
return "--" + string.Join("-", kebabParts).ToLowerInvariant();
}
-
+
///
/// Converts a setting name to dot notation format.
///
@@ -456,7 +477,7 @@ public static string ToDotNotation(string key)
return dotNotation;
}
}
-
+
// If it's a CLI option format
if (IsCLIOptionFormat(key))
{
@@ -464,7 +485,7 @@ public static string ToDotNotation(string key)
{
return dotNotation;
}
-
+
// Remove leading -- and convert kebab-case to PascalCase with dots
var trimmed = key.TrimStart('-');
var parts = trimmed.Split('-');
@@ -475,13 +496,13 @@ public static string ToDotNotation(string key)
parts[i] = char.ToUpper(parts[i][0]) + parts[i].Substring(1).ToLower();
}
}
-
+
return string.Join(".", parts);
}
return key;
}
-
+
///
/// Determines if the given key is in environment variable format.
///
@@ -491,7 +512,7 @@ public static bool IsEnvironmentVariableFormat(string key)
{
return Regex.IsMatch(key, "^[A-Z0-9_]+$");
}
-
+
///
/// Determines if the given key is in CLI option format.
///
@@ -501,7 +522,7 @@ public static bool IsCLIOptionFormat(string key)
{
return key.StartsWith("--");
}
-
+
///
/// Converts a string from PascalCase to kebab-case.
///
@@ -511,7 +532,7 @@ private static string ToKebabCase(string input)
{
if (string.IsNullOrEmpty(input))
return input;
-
+
// Insert a hyphen before each uppercase letter that follows a lowercase letter
var result = Regex.Replace(input, "(? line.Trim()));
diff --git a/src/cycod/ChatClient/FunctionCallingChat.cs b/src/cycod/ChatClient/FunctionCallingChat.cs
index 732f8a975..e9280fa36 100644
--- a/src/cycod/ChatClient/FunctionCallingChat.cs
+++ b/src/cycod/ChatClient/FunctionCallingChat.cs
@@ -13,6 +13,8 @@ public FunctionCallingChat(IChatClient chatClient, string systemPrompt, Function
? chatClient.AsBuilder().UseFunctionInvocation().Build()
: chatClient;
+ var toolsDisabled = (options?.ToolMode ?? ChatToolMode.Auto) == ChatToolMode.None;
+
var tools = _functionFactory.GetAITools().ToList();
ConsoleHelpers.WriteDebugLine($"FunctionCallingChat: Found {tools.Count} tools in FunctionFactory");
@@ -20,8 +22,8 @@ public FunctionCallingChat(IChatClient chatClient, string systemPrompt, Function
_options = new ChatOptions()
{
ModelId = options?.ModelId,
- ToolMode = options?.ToolMode,
- Tools = tools,
+ ToolMode = toolsDisabled ? ChatToolMode.None : (options?.ToolMode ?? ChatToolMode.Auto),
+ Tools = toolsDisabled ? new List() : tools,
MaxOutputTokens = maxOutputTokens.HasValue
? maxOutputTokens.Value
: options?.MaxOutputTokens,
@@ -51,7 +53,7 @@ public void AddUserMessage(string userMessage, int maxPromptTokenTarget = 0, int
maxPromptTokenTarget: maxPromptTokenTarget,
maxChatTokenTarget: maxChatTokenTarget);
}
-
+
public void AddUserMessages(IEnumerable userMessages, int maxPromptTokenTarget = 0, int maxChatTokenTarget = 0)
{
foreach (var userMessage in userMessages)
@@ -109,12 +111,16 @@ public async Task CompleteChatStreamingAsync(
messageCallback?.Invoke(_messages);
var contentToReturn = string.Empty;
+ var toolsDisabled = (_options.ToolMode ?? ChatToolMode.Auto) == ChatToolMode.None;
while (true)
{
var responseContent = string.Empty;
await foreach (var update in _chatClient.GetStreamingResponseAsync(_messages, _options))
{
- _functionCallDetector.CheckForFunctionCall(update);
+ if (!toolsDisabled)
+ {
+ _functionCallDetector.CheckForFunctionCall(update);
+ }
var content = string.Join("", update.Contents
.Where(c => c is TextContent)
@@ -133,7 +139,7 @@ public async Task CompleteChatStreamingAsync(
streamingCallback?.Invoke(update);
}
- if (TryCallFunctions(responseContent, approveFunctionCall, functionCallCallback, messageCallback))
+ if (!toolsDisabled && TryCallFunctions(responseContent, approveFunctionCall, functionCallCallback, messageCallback))
{
_functionCallDetector.Clear();
continue;
@@ -150,7 +156,7 @@ private bool TryCallFunctions(string responseContent, Func
Date: Mon, 25 Aug 2025 20:43:35 -0400
Subject: [PATCH 2/3] readme changes
---
README.md | 2 +
src/cycod/assets/help/configuration.txt | 1 +
src/cycod/assets/help/options.txt | 5 ++
src/cycod/assets/help/provider.txt | 6 ++-
src/cycod/assets/help/settings.txt | 2 +
src/cycod/assets/help/use ollama.txt | 70 +++++++++++++++++++++++++
6 files changed, 85 insertions(+), 1 deletion(-)
create mode 100644 src/cycod/assets/help/use ollama.txt
diff --git a/README.md b/README.md
index 99145552d..a9573a24b 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@ CycoD is a command-line interface (CLI) application that provides a chat-based i
- Run shell commands (Bash, CMD, PowerShell) with persistent sessions
- Manipulate files (view, create, edit, replace text)
- Access date and time information
+ - **GUI Interactions** (Windows only): Show dialogs for user input, file selection, and notifications
- **Comprehensive Configuration System**:
- Multiple configuration scopes (global, user, local)
- Profile support for different configurations
@@ -177,6 +178,7 @@ For more detailed documentation, see:
- [Getting Started](docs/getting-started.md)
- [Command Line Options](docs/cli-options.md)
- [Function Calling](docs/function-calling.md)
+- [GUI Functions](docs/gui-functions.md) - Windows GUI interaction functions
- [Creating Aliases](docs/aliases.md)
- [Chat History](docs/chat-history.md)
- [Slash Commands](#chat-commands) - Special commands during chat sessions
diff --git a/src/cycod/assets/help/configuration.txt b/src/cycod/assets/help/configuration.txt
index c5544dba9..ee6a88365 100644
--- a/src/cycod/assets/help/configuration.txt
+++ b/src/cycod/assets/help/configuration.txt
@@ -136,5 +136,6 @@ SEE ALSO
cycod help use gemini
cycod help use grok
cycod help use openai
+ cycod help use ollama
cycod help function calls
cycod help timeouts
\ No newline at end of file
diff --git a/src/cycod/assets/help/options.txt b/src/cycod/assets/help/options.txt
index f2efc67c1..318345ad7 100644
--- a/src/cycod/assets/help/options.txt
+++ b/src/cycod/assets/help/options.txt
@@ -52,6 +52,7 @@ USAGE: cycod [...]
--use-gemini Prefer use of Google Gemini
--use-grok Prefer use of Grok (x.ai)
--use-openai Prefer use of OpenAI API as the chat provider
+ --use-ollama Prefer use of a local Ollama server (OpenAI-compatible)
ANTHROPIC OPTIONS (see: cycod help use anthropic)
--anthropic-api-key KEY Use a specific API key for Anthropic
@@ -81,6 +82,10 @@ USAGE: cycod [...]
--openai-api-key KEY Use a specific API key
--openai-chat-model-name NAME Use a specific chat model (default: gpt-4o)
+ OLLAMA OPTIONS (see: cycod help use ollama)
+ --ollama-base-url URL Use a specific API base URL (default: http://localhost:11434/v1)
+ --ollama-model-id ID Use a specific model ID (e.g., llama3:8b, qwen2.5:0.5b)
+
COPILOT OPTIONS (see: cycod help use github copilot)
--copilot-model-name NAME Use a specific model by name (default: claude-sonnet-4)
--copilot-api-endpoint URL Use a specific API endpoint (default: https://api.githubcopilot.com)
diff --git a/src/cycod/assets/help/provider.txt b/src/cycod/assets/help/provider.txt
index 2c36eec9e..f66b1b4b1 100644
--- a/src/cycod/assets/help/provider.txt
+++ b/src/cycod/assets/help/provider.txt
@@ -9,6 +9,8 @@ CycoD supports multiple AI providers for chat functionality:
- Google Gemini
- Grok (x.ai)
- OpenAI API
+- GitHub Copilot
+- Ollama (local OpenAI-compatible)
## Provider Selection Flags
@@ -22,6 +24,7 @@ Use these flags to explicitly select which provider to use:
--use-gemini Use Google Gemini
--use-grok Use Grok (x.ai)
--use-openai Use OpenAI API
+--use-ollama Use local Ollama (OpenAI-compatible)
```
## Named Profiles
@@ -69,4 +72,5 @@ CYCOD_PREFERRED_PROVIDER=azure-openai
cycod help use bedrock
cycod help use gemini
cycod help use grok
- cycod help use openai
\ No newline at end of file
+ cycod help use openai
+ cycod help use ollama
\ No newline at end of file
diff --git a/src/cycod/assets/help/settings.txt b/src/cycod/assets/help/settings.txt
index de35f41dc..a0c34b325 100644
--- a/src/cycod/assets/help/settings.txt
+++ b/src/cycod/assets/help/settings.txt
@@ -238,6 +238,7 @@ USE APP SETTINGS
cycod config set App.PreferredProvider gemini
cycod config set App.PreferredProvider grok
cycod config set App.PreferredProvider openai
+ cycod config set App.PreferredProvider ollama
EXAMPLE 2: Set preferred AI provider via environment variable
@@ -259,6 +260,7 @@ USE APP SETTINGS
cycod chat --use-google
cycod chat --use-grok
cycod chat --use-openai
+ cycod chat --use-ollama
SEE ALSO
diff --git a/src/cycod/assets/help/use ollama.txt b/src/cycod/assets/help/use ollama.txt
new file mode 100644
index 000000000..99d6dbfa6
--- /dev/null
+++ b/src/cycod/assets/help/use ollama.txt
@@ -0,0 +1,70 @@
+USE OLLAMA
+
+ AUTHENTICATION
+
+ No API key required for local Ollama.
+
+ QUICK START
+
+ EXAMPLE 1: Pull a tiny model locally (Ollama CLI)
+ ollama pull qwen2.5:0.5b
+
+ EXAMPLE 2: Run a quick test with CycoD
+ cycod chat --use-ollama -q "hello"
+
+ EXAMPLE 3: Point to a non-default server
+ cycod chat --use-ollama --ollama-base-url http://localhost:11434/v1 -q "hi"
+
+ MODEL SELECTION
+
+ EXAMPLE 1: Set model via config
+ cycod config set Ollama.ModelId qwen2.5:0.5b
+ cycod config set Ollama.ModelId llama3:8b --local
+ cycod config set Ollama.ModelId qwen2.5:7b --user
+
+ EXAMPLE 2: Set model via environment variable
+ Set OLLAMA_MODEL_ID environment variable
+
+ EXAMPLE 3: Supply model via command line
+ cycod chat --use-ollama --ollama-model-id qwen2.5:0.5b -q "hello"
+
+ POPULAR MODELS
+
+ EXAMPLE 1: Small (good for low-spec dev boxes)
+ ollama pull qwen2.5:0.5b
+ cycod chat --use-ollama --ollama-model-id qwen2.5:0.5b -q "hello"
+
+ EXAMPLE 2: Mid-size (quality vs speed)
+ ollama pull llama3:8b
+ cycod chat --use-ollama --ollama-model-id llama3:8b -q "summarize why Rust uses a borrow checker"
+
+ EXAMPLE 3: GPT-OSS (larger; needs more memory)
+ ollama pull gpt-oss:20b
+ cycod chat --use-ollama --ollama-model-id gpt-oss:20b -q "explain transformers in simple terms"
+
+ API ENDPOINT (BASE URL)
+
+ EXAMPLE 1: Set base URL via config
+ cycod config set Ollama.BaseUrl http://localhost:11434/v1
+ cycod config set Ollama.BaseUrl http://127.0.0.1:11434/v1 --local
+ cycod config set Ollama.BaseUrl http://my-host:11434/v1 --user
+
+ EXAMPLE 2: Set base URL via environment variable
+ Set OLLAMA_BASE_URL environment variable (default: http://localhost:11434/v1)
+
+ EXAMPLE 3: Supply base URL via command line
+ cycod chat --use-ollama --ollama-base-url http://localhost:11434/v1 -q "hello"
+
+ FUNCTION CALLING
+
+ In this release, CycoD uses Ollama for chat completion only.
+ Function tools are not enabled with Ollama.
+
+ TROUBLESHOOTING
+
+ - model "X" not found
+ Run: ollama pull
+
+ - out of memory / slow generation
+ Try a smaller model (e.g., qwen2.5:0.5b or llama3:8b), close other GPU apps,
+ or use a quantized/compact variant if available.
From 08ec7bb190d9176bc5ab2bf08c98dc136818ce74 Mon Sep 17 00:00:00 2001
From: emilyjiji <92887308+emilyjiji@users.noreply.github.com>
Date: Wed, 27 Aug 2025 22:40:56 -0400
Subject: [PATCH 3/3] revert unrelated code
---
README.md | 2 --
1 file changed, 2 deletions(-)
diff --git a/README.md b/README.md
index a9573a24b..99145552d 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,6 @@ CycoD is a command-line interface (CLI) application that provides a chat-based i
- Run shell commands (Bash, CMD, PowerShell) with persistent sessions
- Manipulate files (view, create, edit, replace text)
- Access date and time information
- - **GUI Interactions** (Windows only): Show dialogs for user input, file selection, and notifications
- **Comprehensive Configuration System**:
- Multiple configuration scopes (global, user, local)
- Profile support for different configurations
@@ -178,7 +177,6 @@ For more detailed documentation, see:
- [Getting Started](docs/getting-started.md)
- [Command Line Options](docs/cli-options.md)
- [Function Calling](docs/function-calling.md)
-- [GUI Functions](docs/gui-functions.md) - Windows GUI interaction functions
- [Creating Aliases](docs/aliases.md)
- [Chat History](docs/chat-history.md)
- [Slash Commands](#chat-commands) - Special commands during chat sessions