summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-26 09:05:36 +0300
committerPaul Buetow <paul@buetow.org>2026-04-26 09:05:36 +0300
commit017b355d6632ef9fbf162fa741e7dde366b2b2db (patch)
treeee23cbde7c06cdff659820565b13be608eed49aa /internal
parent4e59e25a4304a674a7c970bc371640cf0f73d3fd (diff)
feat: default to Ollama Cloud (kimi-k2.6) when no provider configured
Switches the in-code defaults so that hexai talks to Ollama Cloud (https://ollama.com) with model kimi-k2.6 when no provider is configured, instead of OpenAI. The example config, README, and configuration guide all reflect the new recommended setup; previous OpenAI / local-Ollama options are still documented as alternatives. Tests that depended on the implicit "openai" default now pin the provider explicitly so they continue to exercise the OpenAI / gpt-5 code paths they were designed to cover. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'internal')
-rw-r--r--internal/hexaicli/cache_test.go3
-rw-r--r--internal/hexaicli/run_test.go4
-rw-r--r--internal/hexailsp/run_test.go10
-rw-r--r--internal/llm/ollama.go7
-rw-r--r--internal/llm/provider.go2
-rw-r--r--internal/llmutils/client.go7
-rw-r--r--internal/llmutils/client_test.go4
-rw-r--r--internal/lsp/llm_request_opts_test.go3
8 files changed, 31 insertions, 9 deletions
diff --git a/internal/hexaicli/cache_test.go b/internal/hexaicli/cache_test.go
index 5f00e7b..c9b83c6 100644
--- a/internal/hexaicli/cache_test.go
+++ b/internal/hexaicli/cache_test.go
@@ -75,6 +75,9 @@ func TestRun_UsesCachedResponseWithoutClientCall(t *testing.T) {
t.Chdir(t.TempDir())
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
t.Setenv("XDG_CACHE_HOME", t.TempDir())
+ // This test asserts an "openai/gpt-4.1" cache-hit label, so pin the provider
+ // (the in-code default switched to ollama when no config is present).
+ t.Setenv("HEXAI_PROVIDER", "openai")
oldNew := newClientFromApp
defer func() { newClientFromApp = oldNew }()
diff --git a/internal/hexaicli/run_test.go b/internal/hexaicli/run_test.go
index 69e5d98..e2788e5 100644
--- a/internal/hexaicli/run_test.go
+++ b/internal/hexaicli/run_test.go
@@ -167,6 +167,10 @@ func TestPrintProviderInfo(t *testing.T) {
}
func TestRun_SingleProviderHeaderUsesStderr(t *testing.T) {
+ // This test asserts an "openai:gpt-4.1:" header, so pin the provider/model
+ // explicitly (the in-code default switched to ollama/kimi-k2.6).
+ t.Setenv("HEXAI_PROVIDER", "openai")
+ t.Setenv("HEXAI_OPENAI_MODEL", "gpt-4.1")
oldNew := newClientFromApp
defer func() { newClientFromApp = oldNew }()
newClientFromApp = func(_ appconfig.App) (llm.Client, error) {
diff --git a/internal/hexailsp/run_test.go b/internal/hexailsp/run_test.go
index badb27c..fa4d535 100644
--- a/internal/hexailsp/run_test.go
+++ b/internal/hexailsp/run_test.go
@@ -38,6 +38,11 @@ func TestRunWithFactory_UsesDefaultsAndCallsServer(t *testing.T) {
var stderr bytes.Buffer
logger := log.New(&stderr, "hexai-lsp-server ", 0)
cfg := appconfig.Load(nil) // defaults
+ // Pin provider to openai: the in-code default is now ollama, which would
+ // happily build a client without a key and short-circuit the missing-key
+ // assertion below. Load(nil) returns raw defaults and ignores env vars,
+ // so set the field directly on the struct.
+ cfg.Provider = "openai"
var gotOpts lsp.ServerOptions
factory := func(r io.Reader, w io.Writer, logger *log.Logger, opts lsp.ServerOptions) ServerRunner {
gotOpts = opts
@@ -75,7 +80,10 @@ func TestRunWithFactory_BuildsClientWhenKeysPresent(t *testing.T) {
var stderr bytes.Buffer
logger := log.New(&stderr, "hexai-lsp-server ", 0)
- cfg := appconfig.Load(nil) // defaults, provider=openai by default
+ cfg := appconfig.Load(nil) // defaults
+ // Pin provider to openai (the in-code default is now ollama). Load(nil)
+ // returns raw defaults and ignores env vars, so set this on the struct.
+ cfg.Provider = "openai"
var got llm.Client
factory := func(r io.Reader, w io.Writer, logger *log.Logger, opts lsp.ServerOptions) ServerRunner {
got = opts.Client
diff --git a/internal/llm/ollama.go b/internal/llm/ollama.go
index 0916c06..987a258 100644
--- a/internal/llm/ollama.go
+++ b/internal/llm/ollama.go
@@ -66,11 +66,14 @@ func newOllama(baseURL, model string, defaultTemp *float64, apiKey string) Clien
}
func newOllamaWithTimeout(baseURL, model, apiKey string, defaultTemp *float64, timeoutSec int) Client {
+ // Defaults target Ollama Cloud (ollama.ai); a local server is opted into
+ // by setting base_url = "http://localhost:11434" (or HEXAI_OLLAMA_BASE_URL)
+ // and an appropriate model.
if strings.TrimSpace(baseURL) == "" {
- baseURL = "http://localhost:11434"
+ baseURL = "https://ollama.com"
}
if strings.TrimSpace(model) == "" {
- model = "qwen3-coder:30b-a3b-q4_K_M"
+ model = "kimi-k2.6"
}
if timeoutSec <= 0 {
timeoutSec = 30
diff --git a/internal/llm/provider.go b/internal/llm/provider.go
index 255297c..d1ff404 100644
--- a/internal/llm/provider.go
+++ b/internal/llm/provider.go
@@ -152,7 +152,7 @@ func RegisterAllProviders() {
func NewFromConfig(cfg Config, openAIAPIKey, openRouterAPIKey, anthropicAPIKey, ollamaAPIKey string) (Client, error) {
provider := normalizeProvider(cfg.Provider)
if provider == "" {
- provider = "openai"
+ provider = "ollama"
}
factory, ok := lookupProviderFactory(provider)
diff --git a/internal/llmutils/client.go b/internal/llmutils/client.go
index ef24571..ccba847 100644
--- a/internal/llmutils/client.go
+++ b/internal/llmutils/client.go
@@ -8,11 +8,12 @@ import (
"codeberg.org/snonux/hexai/internal/llm"
)
-// CanonicalProvider normalizes provider names and defaults to openai.
+// CanonicalProvider normalizes provider names and defaults to ollama (Ollama
+// Cloud at https://ollama.com when paired with the default base URL).
func CanonicalProvider(name string) string {
provider := strings.ToLower(strings.TrimSpace(name))
if provider == "" {
- return "openai"
+ return "ollama"
}
return provider
}
@@ -29,7 +30,7 @@ func DefaultModelForProvider(cfg appconfig.App, provider string) string {
if model := strings.TrimSpace(cfg.OllamaModel); model != "" {
return model
}
- return "qwen3-coder:30b-a3b-q4_K_M"
+ return "kimi-k2.6"
case "anthropic":
if model := strings.TrimSpace(cfg.AnthropicModel); model != "" {
return model
diff --git a/internal/llmutils/client_test.go b/internal/llmutils/client_test.go
index c688213..ed91584 100644
--- a/internal/llmutils/client_test.go
+++ b/internal/llmutils/client_test.go
@@ -37,7 +37,7 @@ func TestCanonicalProvider(t *testing.T) {
if got := CanonicalProvider(" OpenRouter "); got != "openrouter" {
t.Fatalf("CanonicalProvider(openrouter) = %q", got)
}
- if got := CanonicalProvider(" "); got != "openai" {
+ if got := CanonicalProvider(" "); got != "ollama" {
t.Fatalf("CanonicalProvider(empty) = %q", got)
}
}
@@ -73,7 +73,7 @@ func TestDefaultModelForProvider_Fallbacks(t *testing.T) {
if got := DefaultModelForProvider(cfg, "openrouter"); got != "openrouter/auto" {
t.Fatalf("openrouter fallback = %q", got)
}
- if got := DefaultModelForProvider(cfg, "ollama"); got != "qwen3-coder:30b-a3b-q4_K_M" {
+ if got := DefaultModelForProvider(cfg, "ollama"); got != "kimi-k2.6" {
t.Fatalf("ollama fallback = %q", got)
}
if got := DefaultModelForProvider(cfg, "anthropic"); got != "claude-3-5-sonnet-20240620" {
diff --git a/internal/lsp/llm_request_opts_test.go b/internal/lsp/llm_request_opts_test.go
index ad87cd4..79a83fc 100644
--- a/internal/lsp/llm_request_opts_test.go
+++ b/internal/lsp/llm_request_opts_test.go
@@ -24,6 +24,9 @@ func TestRequestSpec_Gpt5_ForcesTemp1(t *testing.T) {
s := newTestServer()
one := 0.2
s.cfg.CodingTemperature = &one
+ // Pin Provider explicitly: the in-code default is now ollama, but the
+ // gpt-5 temperature-force rule only fires for openai.
+ s.cfg.Provider = "openai"
s.llmClient = fakeClient{name: "openai", model: "gpt-5.0"}
s.cfg.OpenAIModel = "gpt-5.0"