mirror of
https://github.com/lukaszraczylo/harness-sync.git
synced 2026-10-17 01:17:35 +00:00
feat: URL-derived provider keys (hs-<hostname>) + dedup providers sharing same gateway URL
- GatewayProviderKey() derives stable key from gateway URL hostname e.g. https://llmgw.h.raczylo.com/v1 → hs-llmgw-h-raczylo-com - AbsorbDuplicateProviders() merges providers sharing the same baseURL into our canonical entry; user-crafted model metadata wins for overlaps - kilo: renders to opencode.jsonc (primary config), deep-merges providers, deletes enabled_providers filter that was hiding our gateway - opencode: absorbs duplicate providers from existing config - All adapters use GatewayProviderKey() instead of hardcoded string - Fix goose URL doubling (was /v1/v1/chat/completions → /chat/completions) - Fix token propagation: api_key written directly (not env var ref) - Zed: adds prompt_cache_key capability, deletes context_servers key to avoid ContextServerSettingsContent serde untagged-enum parse error
This commit is contained in:
@@ -89,11 +89,9 @@ func (a *Adapter) Render(b *canonical.Bundle) (*adapter.FileSet, error) {
|
||||
mcps[s.Name] = e
|
||||
}
|
||||
|
||||
provKey := common.GatewayProviderKey(b.Profile.Gateway.URL)
|
||||
// inline model shorthand: "providerID/modelID"
|
||||
modelRef := ""
|
||||
if b.Profile.Gateway.DefaultModel != "" {
|
||||
modelRef = "harness-sync-gateway/" + b.Profile.Gateway.DefaultModel
|
||||
}
|
||||
modelRef := common.KiloModelString(&b.Profile)
|
||||
|
||||
cfg := map[string]any{
|
||||
"version": 8,
|
||||
@@ -108,8 +106,8 @@ func (a *Adapter) Render(b *canonical.Bundle) (*adapter.FileSet, error) {
|
||||
},
|
||||
"providers": common.ProvidersAsCagentMap(&b.Profile),
|
||||
"models": map[string]any{
|
||||
"harness-sync-gateway": map[string]any{
|
||||
"provider": "harness-sync-gateway",
|
||||
provKey: map[string]any{
|
||||
"provider": provKey,
|
||||
"model": b.Profile.Gateway.DefaultModel,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -48,20 +48,20 @@ func TestCagentRenderProducesExpectedTargets(t *testing.T) {
|
||||
|
||||
agents := parsed["agents"].(map[string]any)
|
||||
def := agents["default"].(map[string]any)
|
||||
assert.Equal(t, "harness-sync-gateway/claude-sonnet-4-6", def["model"])
|
||||
assert.Equal(t, "hs-gw/claude-sonnet-4-6", def["model"])
|
||||
assert.Contains(t, def["instruction"], "# global instruction")
|
||||
|
||||
// providers map (new schema).
|
||||
providers := parsed["providers"].(map[string]any)
|
||||
gwProv := providers["harness-sync-gateway"].(map[string]any)
|
||||
gwProv := providers["hs-gw"].(map[string]any)
|
||||
assert.Equal(t, "https://gw", gwProv["base_url"])
|
||||
assert.Equal(t, "openai", gwProv["provider"])
|
||||
assert.NotContains(t, gwProv, "token_key")
|
||||
|
||||
// models map.
|
||||
models := parsed["models"].(map[string]any)
|
||||
gw := models["harness-sync-gateway"].(map[string]any)
|
||||
assert.Equal(t, "harness-sync-gateway", gw["provider"])
|
||||
gw := models["hs-gw"].(map[string]any)
|
||||
assert.Equal(t, "hs-gw", gw["provider"])
|
||||
assert.Equal(t, "claude-sonnet-4-6", gw["model"])
|
||||
|
||||
mcps := parsed["mcps"].(map[string]any)
|
||||
@@ -93,7 +93,7 @@ func TestCagentRenderProvidersAndModelsMaps(t *testing.T) {
|
||||
// providers block present.
|
||||
providers, ok := parsed["providers"].(map[string]any)
|
||||
require.True(t, ok, "providers must be a map")
|
||||
gw, ok := providers["harness-sync-gateway"].(map[string]any)
|
||||
gw, ok := providers["hs-gw"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "https://gw", gw["base_url"])
|
||||
assert.Equal(t, "tok", gw["api_key"])
|
||||
@@ -103,7 +103,7 @@ func TestCagentRenderProvidersAndModelsMaps(t *testing.T) {
|
||||
// agents.default.model uses inline shorthand.
|
||||
agents := parsed["agents"].(map[string]any)
|
||||
def := agents["default"].(map[string]any)
|
||||
assert.Equal(t, "harness-sync-gateway/claude-sonnet-4-6", def["model"])
|
||||
assert.Equal(t, "hs-gw/claude-sonnet-4-6", def["model"])
|
||||
}
|
||||
|
||||
func TestCagentRenderNoMCP(t *testing.T) {
|
||||
|
||||
@@ -44,8 +44,8 @@ func TestCrushRenderProducesExpectedTargets(t *testing.T) {
|
||||
// providers must be a map (not array), keyed by providerID.
|
||||
providers, ok := parsed["providers"].(map[string]any)
|
||||
require.True(t, ok, "providers must be a map")
|
||||
gw, ok := providers["harness-sync-gateway"].(map[string]any)
|
||||
require.True(t, ok, "harness-sync-gateway provider missing")
|
||||
gw, ok := providers["hs-gw"].(map[string]any)
|
||||
require.True(t, ok, "hs-gw provider missing")
|
||||
assert.Equal(t, "openai-compat", gw["type"])
|
||||
// models must be role-map with large/small/title.
|
||||
models, ok := parsed["models"].(map[string]any)
|
||||
@@ -137,7 +137,7 @@ func TestCrushRenderProducesMapProvidersAndRoleModels(t *testing.T) {
|
||||
// providers is a map, not an array.
|
||||
providers, ok := parsed["providers"].(map[string]any)
|
||||
require.True(t, ok, "providers must be a map")
|
||||
gw, ok := providers["harness-sync-gateway"].(map[string]any)
|
||||
gw, ok := providers["hs-gw"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "openai-compat", gw["type"])
|
||||
assert.Equal(t, "https://gw", gw["base_url"])
|
||||
@@ -160,7 +160,7 @@ func TestCrushRenderProducesMapProvidersAndRoleModels(t *testing.T) {
|
||||
sel, ok := roleModels[role].(map[string]any)
|
||||
require.True(t, ok, "role %s missing", role)
|
||||
assert.Equal(t, "claude-sonnet-4-6", sel["model"])
|
||||
assert.Equal(t, "harness-sync-gateway", sel["provider"])
|
||||
assert.Equal(t, "hs-gw", sel["provider"])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ func TestGooseRenderGatewayURL(t *testing.T) {
|
||||
var parsed map[string]any
|
||||
require.NoError(t, yaml.Unmarshal(seen[cfgDest].Content, &parsed))
|
||||
// Custom gateway → GOOSE_PROVIDER is the custom provider name.
|
||||
assert.Equal(t, "custom_harness-sync-gateway", parsed["GOOSE_PROVIDER"])
|
||||
assert.Equal(t, "custom_hs-gw", parsed["GOOSE_PROVIDER"])
|
||||
}
|
||||
|
||||
func TestGooseRenderProducesCustomProviderFile(t *testing.T) {
|
||||
@@ -109,13 +109,13 @@ func TestGooseRenderProducesCustomProviderFile(t *testing.T) {
|
||||
seen := map[string]adapter.File{}
|
||||
fs.ForEach(func(f adapter.File) { seen[f.Dest] = f })
|
||||
|
||||
cpDest := filepath.Join(home, ".config", "goose", "custom_providers", "custom_harness-sync-gateway.json")
|
||||
cpDest := filepath.Join(home, ".config", "goose", "custom_providers", "custom_hs-gw.json")
|
||||
require.Contains(t, seen, cpDest)
|
||||
assert.Equal(t, adapter.RenderedFile, seen[cpDest].Kind)
|
||||
|
||||
var cp map[string]any
|
||||
require.NoError(t, json.Unmarshal(seen[cpDest].Content, &cp))
|
||||
assert.Equal(t, "custom_harness-sync-gateway", cp["name"])
|
||||
assert.Equal(t, "custom_hs-gw", cp["name"])
|
||||
assert.Equal(t, "openai", cp["engine"])
|
||||
assert.Equal(t, "tok", cp["api_key"])
|
||||
assert.NotContains(t, cp, "api_key_env")
|
||||
|
||||
@@ -126,14 +126,18 @@ func (a *Adapter) Render(b *canonical.Bundle) (*adapter.FileSet, error) {
|
||||
"enabled_providers": nil, // delete filter
|
||||
}
|
||||
|
||||
// Deep-merge provider: add our entries INTO the existing provider map.
|
||||
// Deep-merge provider: add our entries INTO the existing provider map,
|
||||
// then absorb any duplicate providers that share the same gateway URL.
|
||||
if newProv, ok := overlay["provider"].(map[string]any); ok && len(newProv) > 0 {
|
||||
existing, _ := ocBase["provider"].(map[string]any)
|
||||
if existing == nil {
|
||||
existing = map[string]any{}
|
||||
existingProv, _ := ocBase["provider"].(map[string]any)
|
||||
if existingProv == nil {
|
||||
existingProv = map[string]any{}
|
||||
}
|
||||
maps.Copy(existing, newProv)
|
||||
ocOverlay["provider"] = existing
|
||||
maps.Copy(existingProv, newProv)
|
||||
// Absorb providers at the same URL into harness-sync-gateway,
|
||||
// preserving user-crafted display names and custom limits.
|
||||
merged := common.AbsorbDuplicateProviders(existingProv, common.GatewayProviderKey(b.Profile.Gateway.URL), b.Profile.Gateway.URL)
|
||||
ocOverlay["provider"] = merged
|
||||
}
|
||||
// Copy model/small_model/mcp from the kilo.json overlay.
|
||||
for _, k := range []string{"model", "small_model", "mcp"} {
|
||||
|
||||
@@ -43,12 +43,12 @@ func TestKiloRender(t *testing.T) {
|
||||
var parsed map[string]any
|
||||
require.NoError(t, json.Unmarshal(seen[cfgDest].Content, &parsed))
|
||||
// model is "providerID/modelID" string.
|
||||
assert.Equal(t, "harness-sync-gateway/sonnet", parsed["model"])
|
||||
assert.Equal(t, "harness-sync-gateway/sonnet", parsed["small_model"])
|
||||
assert.Equal(t, "hs-gw/sonnet", parsed["model"])
|
||||
assert.Equal(t, "hs-gw/sonnet", parsed["small_model"])
|
||||
// provider map present with npm field.
|
||||
provider, ok := parsed["provider"].(map[string]any)
|
||||
require.True(t, ok, "provider must be a map")
|
||||
gw, ok := provider["harness-sync-gateway"].(map[string]any)
|
||||
gw, ok := provider["hs-gw"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "@ai-sdk/openai-compatible", gw["npm"])
|
||||
assert.Contains(t, parsed, "mcp")
|
||||
@@ -113,14 +113,14 @@ func TestKiloRenderEmitsModelString(t *testing.T) {
|
||||
var parsed map[string]any
|
||||
require.NoError(t, json.Unmarshal(seen[cfgDest].Content, &parsed))
|
||||
|
||||
// model must be "harness-sync-gateway/<modelID>".
|
||||
assert.Equal(t, "harness-sync-gateway/claude-sonnet-4-6", parsed["model"])
|
||||
assert.Equal(t, "harness-sync-gateway/claude-sonnet-4-6", parsed["small_model"])
|
||||
// model must be "hs-gw/<modelID>".
|
||||
assert.Equal(t, "hs-gw/claude-sonnet-4-6", parsed["model"])
|
||||
assert.Equal(t, "hs-gw/claude-sonnet-4-6", parsed["small_model"])
|
||||
|
||||
// provider map has npm field.
|
||||
provider, ok := parsed["provider"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
gw, ok := provider["harness-sync-gateway"].(map[string]any)
|
||||
gw, ok := provider["hs-gw"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "@ai-sdk/openai-compatible", gw["npm"])
|
||||
opts, ok := gw["options"].(map[string]any)
|
||||
@@ -165,9 +165,9 @@ func TestKiloRenderMergesOpenCodeJSONC(t *testing.T) {
|
||||
// llmgw preserved, harness-sync-gateway added.
|
||||
provider := parsed["provider"].(map[string]any)
|
||||
assert.Contains(t, provider, "llmgw")
|
||||
assert.Contains(t, provider, "harness-sync-gateway")
|
||||
assert.Contains(t, provider, "hs-gw")
|
||||
// model updated.
|
||||
assert.Equal(t, "harness-sync-gateway/sonnet", parsed["model"])
|
||||
assert.Equal(t, "hs-gw/sonnet", parsed["model"])
|
||||
}
|
||||
|
||||
func TestKiloImport(t *testing.T) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package opencode
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -76,8 +77,27 @@ func (a *Adapter) Render(b *canonical.Bundle) (*adapter.FileSet, error) {
|
||||
|
||||
cfgPath := filepath.Join(base, "opencode.jsonc")
|
||||
existing, _ := os.ReadFile(cfgPath)
|
||||
|
||||
// Build the provider map, then absorb any existing providers at the same
|
||||
// gateway URL so duplicates don't accumulate across apply runs.
|
||||
provMap := common.ProvidersAsMap(&b.Profile)
|
||||
if len(existing) > 0 {
|
||||
var base map[string]any
|
||||
clean := common.StripJSONComments(string(existing))
|
||||
if json.Unmarshal([]byte(clean), &base) == nil {
|
||||
if existingProv, ok := base["provider"].(map[string]any); ok {
|
||||
for k, v := range existingProv {
|
||||
if _, ours := provMap[k]; !ours {
|
||||
provMap[k] = v
|
||||
}
|
||||
}
|
||||
provMap = common.AbsorbDuplicateProviders(provMap, common.GatewayProviderKey(b.Profile.Gateway.URL), b.Profile.Gateway.URL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
overlay := map[string]any{
|
||||
"provider": common.ProvidersAsMap(&b.Profile),
|
||||
"provider": provMap,
|
||||
"model": common.KiloModelString(&b.Profile),
|
||||
}
|
||||
if mcp := common.BuildMCPMapStyled(&b.MCP, common.MCPOpencodeStyle); len(mcp) > 0 {
|
||||
|
||||
@@ -129,15 +129,15 @@ func TestOpencodeRenderProviderHasNpm(t *testing.T) {
|
||||
// provider map has npm field.
|
||||
provider, ok := parsed["provider"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
gw, ok := provider["harness-sync-gateway"].(map[string]any)
|
||||
gw, ok := provider["hs-gw"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "@ai-sdk/openai-compatible", gw["npm"])
|
||||
opts, ok := gw["options"].(map[string]any)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "https://gw", opts["baseURL"])
|
||||
|
||||
// model is "harness-sync-gateway/modelID".
|
||||
assert.Equal(t, "harness-sync-gateway/claude-sonnet-4-6", parsed["model"])
|
||||
// model is "hs-gw/modelID".
|
||||
assert.Equal(t, "hs-gw/claude-sonnet-4-6", parsed["model"])
|
||||
}
|
||||
|
||||
func TestOpencodeImportStripsComments(t *testing.T) {
|
||||
|
||||
@@ -136,7 +136,7 @@ func zedLanguageModels(p *canonical.Profile) map[string]any {
|
||||
}
|
||||
return map[string]any{
|
||||
"openai_compatible": map[string]any{
|
||||
common.GatewayProviderID: entry,
|
||||
common.GatewayProviderKey(p.Gateway.URL): entry,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func zedAgentBlock(existing []byte, p *canonical.Profile) map[string]any {
|
||||
}
|
||||
if p.Gateway.DefaultModel != "" {
|
||||
base["default_model"] = map[string]any{
|
||||
"provider": common.GatewayProviderID,
|
||||
"provider": common.GatewayProviderKey(p.Gateway.URL),
|
||||
"model": p.Gateway.DefaultModel,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,16 +46,16 @@ func TestZedRenderProducesExpectedTargets(t *testing.T) {
|
||||
var parsed map[string]any
|
||||
require.NoError(t, json.Unmarshal(seen[cfgDest].Content, &parsed))
|
||||
|
||||
// agent.default_model routes through harness-sync-gateway (openai_compatible provider).
|
||||
// agent.default_model routes through hs-llmgw-example-com (openai_compatible provider).
|
||||
agent := parsed["agent"].(map[string]any)
|
||||
dm := agent["default_model"].(map[string]any)
|
||||
assert.Equal(t, "harness-sync-gateway", dm["provider"])
|
||||
assert.Equal(t, "hs-llmgw-example-com", dm["provider"])
|
||||
assert.Equal(t, "claude-sonnet-4-6", dm["model"])
|
||||
|
||||
// language_models.openai_compatible has our named gateway entry.
|
||||
lm := parsed["language_models"].(map[string]any)
|
||||
oc := lm["openai_compatible"].(map[string]any)
|
||||
gw := oc["harness-sync-gateway"].(map[string]any)
|
||||
gw := oc["hs-llmgw-example-com"].(map[string]any)
|
||||
assert.Equal(t, "https://llmgw.example.com/v1", gw["api_url"])
|
||||
|
||||
// context_servers must be absent — Zed serde untagged-enum bug aborts
|
||||
@@ -140,8 +140,8 @@ func TestZedRenderEmitsLanguageModelsOpenAICompatible(t *testing.T) {
|
||||
require.True(t, ok, "language_models must be present")
|
||||
oc, ok := lm["openai_compatible"].(map[string]any)
|
||||
require.True(t, ok, "language_models.openai_compatible must be present")
|
||||
gw, ok := oc["harness-sync-gateway"].(map[string]any)
|
||||
require.True(t, ok, "openai_compatible.harness-sync-gateway must be present")
|
||||
gw, ok := oc["hs-gw"].(map[string]any)
|
||||
require.True(t, ok, "openai_compatible.hs-gw must be present")
|
||||
assert.Equal(t, "https://gw", gw["api_url"])
|
||||
avail, ok := gw["available_models"].([]any)
|
||||
require.True(t, ok)
|
||||
|
||||
Reference in New Issue
Block a user