feat: claude-code backends + llamaswap provider + dogfood the CC engine (#3)
Build & push image / build-and-push (push) Successful in 10s

Phase 2: bump majordomo to latest and wire its new llamaswap provider
into gadfly's endpoint switches; add claude-code/sonnet to gadfly's own
dogfood swarm (pin :sha-86f12c1, map CLAUDE_CODE_OAUTH_TOKEN) so the
Phase-1 engine runs as a live competitor; document the Ollama-through-CC
ANTHROPIC_BASE_URL proxy path as example-only.

The 11-model swarm (incl. claude-code/sonnet) reviewed it; 52 findings
graded via the MCP. Folded in the two real ones: a llamaswap
endpointProvider test (caught by claude-code/sonnet, citing CLAUDE.md)
and adding "openai-compatible" to the provider error messages (gpt-oss).

gofmt clean, go vet quiet, go build + go test -race green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Steve Dudenhoeffer <steve@stevedudenhoeffer.com>
Co-committed-by: Steve Dudenhoeffer <steve@stevedudenhoeffer.com>
This commit was merged in pull request #3.
This commit is contained in:
2026-06-27 21:53:41 +00:00
committed by steve
parent 86f12c126f
commit 82f7ef78d5
7 changed files with 175 additions and 159 deletions
+16 -3
View File
@@ -9,6 +9,7 @@ import (
llm "gitea.stevedudenhoeffer.com/steve/majordomo/llm"
"gitea.stevedudenhoeffer.com/steve/majordomo/provider/anthropic"
"gitea.stevedudenhoeffer.com/steve/majordomo/provider/google"
"gitea.stevedudenhoeffer.com/steve/majordomo/provider/llamaswap"
"gitea.stevedudenhoeffer.com/steve/majordomo/provider/ollama"
"gitea.stevedudenhoeffer.com/steve/majordomo/provider/openai"
)
@@ -79,6 +80,12 @@ func resolveModel() (llm.Model, error) {
opts = append(opts, ollama.WithToken(apiKey))
}
return ollama.New(opts...).Model(model)
case "llamaswap":
opts := []llamaswap.Option{llamaswap.WithBaseURL(baseURL)}
if apiKey != "" {
opts = append(opts, llamaswap.WithToken(apiKey))
}
return llamaswap.New(opts...).Model(model)
case "foreman":
// foreman (gitea.stevedudenhoeffer.com/steve/foreman) is a native-Ollama
// queue daemon; the preset also smooths its non-streaming/long-poll
@@ -97,7 +104,7 @@ func resolveModel() (llm.Model, error) {
}
return google.New(opts...).Model(model)
default:
return nil, fmt.Errorf("GADFLY_BASE_URL is set but GADFLY_PROVIDER %q has no endpoint-override support (use openai/ollama/foreman/anthropic/google, or unset GADFLY_BASE_URL to resolve via majordomo)", provider)
return nil, fmt.Errorf("GADFLY_BASE_URL is set but GADFLY_PROVIDER %q has no endpoint-override support (use openai/openai-compatible/ollama/llamaswap/foreman/anthropic/google, or unset GADFLY_BASE_URL to resolve via majordomo)", provider)
}
}
@@ -156,7 +163,7 @@ func modelProvider() string {
// plaintext local Ollama (or foreman queue) works:
// GADFLY_ENDPOINT_BIGBOX="ollama|http://192.168.1.50:11434"
// GADFLY_MODEL=bigbox/qwen2.5-coder:7b
// provider is one of ollama/foreman/openai/anthropic/google; "foreman"
// provider is one of ollama/llamaswap/foreman/openai/anthropic/google; "foreman"
// targets a foreman daemon (native Ollama on the wire):
// GADFLY_ENDPOINT_M1="foreman|http://foreman-m1:8080|tok"
//
@@ -215,6 +222,12 @@ func endpointProvider(name, raw string) (llm.Provider, error) {
opts = append(opts, ollama.WithToken(key))
}
return ollama.New(opts...), nil
case "llamaswap":
opts := []llamaswap.Option{llamaswap.WithName(name), llamaswap.WithBaseURL(baseURL)}
if key != "" {
opts = append(opts, llamaswap.WithToken(key))
}
return llamaswap.New(opts...), nil
case "foreman":
// foreman is native-Ollama on the wire; the preset additionally handles
// its non-streaming degradation. Unlike the HTTPS-only LLM_* foreman://
@@ -239,6 +252,6 @@ func endpointProvider(name, raw string) (llm.Provider, error) {
}
return google.New(opts...), nil
default:
return nil, fmt.Errorf("unknown provider %q (use ollama/foreman/openai/anthropic/google)", provider)
return nil, fmt.Errorf("unknown provider %q (use ollama/llamaswap/foreman/openai/openai-compatible/anthropic/google)", provider)
}
}
+14
View File
@@ -32,6 +32,20 @@ func TestEndpointProvider(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
})
t.Run("llamaswap registers under its name", func(t *testing.T) {
p, err := endpointProvider("ls", "llamaswap|http://swap.lan:8080|tok")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if p.Name() != "ls" {
t.Errorf("Name() = %q, want %q", p.Name(), "ls")
}
})
t.Run("llamaswap without token", func(t *testing.T) {
if _, err := endpointProvider("ls2", "llamaswap|http://swap.lan:8080"); err != nil {
t.Fatalf("unexpected error: %v", err)
}
})
for _, bad := range []string{"", "ollama", "noprovider-no-pipe", "mystery|http://x"} {
t.Run("rejects "+bad, func(t *testing.T) {
if _, err := endpointProvider("n", bad); err == nil {