mirror of
https://github.com/lukaszraczylo/harness-sync.git
synced 2026-10-12 00:45:22 +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:
@@ -3,15 +3,36 @@ package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/lukaszraczylo/harness-sync/internal/canonical"
|
||||
)
|
||||
|
||||
const gatewayProviderID = "harness-sync-gateway"
|
||||
const fallbackGatewayProviderID = "harness-sync-gateway"
|
||||
|
||||
// GatewayProviderID is the canonical provider key used across all adapters.
|
||||
const GatewayProviderID = gatewayProviderID
|
||||
// Deprecated: prefer GatewayProviderKey(gatewayURL) for a stable URL-derived key.
|
||||
const GatewayProviderID = fallbackGatewayProviderID
|
||||
|
||||
// GatewayProviderKey returns a stable provider key derived from the gateway URL.
|
||||
// Format: "hs-<hostname>" with dots and special chars normalised to dashes.
|
||||
// Falls back to "harness-sync-gateway" when the URL is empty or unparseable.
|
||||
//
|
||||
// https://llmgw.h.raczylo.com/v1 → hs-llmgw-h-raczylo-com
|
||||
func GatewayProviderKey(gatewayURL string) string {
|
||||
if gatewayURL == "" {
|
||||
return fallbackGatewayProviderID
|
||||
}
|
||||
u, err := url.Parse(gatewayURL)
|
||||
if err != nil || u.Hostname() == "" {
|
||||
return fallbackGatewayProviderID
|
||||
}
|
||||
host := u.Hostname()
|
||||
// Replace dots, underscores, and colons (port separators) with dashes.
|
||||
r := strings.NewReplacer(".", "-", "_", "-", ":", "-")
|
||||
return "hs-" + r.Replace(host)
|
||||
}
|
||||
|
||||
// ProviderEntry is a generic provider record. Adapters may massage further.
|
||||
type ProviderEntry = map[string]any
|
||||
@@ -22,8 +43,9 @@ type ProviderEntry = map[string]any
|
||||
func BuildProviders(p *canonical.Profile) []ProviderEntry {
|
||||
out := make([]ProviderEntry, 0, len(p.Upstreams)+1)
|
||||
if p.Gateway.URL != "" {
|
||||
key := GatewayProviderKey(p.Gateway.URL)
|
||||
gw := ProviderEntry{
|
||||
"id": gatewayProviderID,
|
||||
"id": key,
|
||||
"name": "harness-sync gateway",
|
||||
"base_url": p.Gateway.URL,
|
||||
"api_key": p.Gateway.Token,
|
||||
@@ -89,7 +111,7 @@ func ProvidersAsCrushMap(p *canonical.Profile) map[string]any {
|
||||
if len(models) == 0 && p.Gateway.DefaultModel != "" {
|
||||
models = append(models, crushModel(p.Gateway.DefaultModel))
|
||||
}
|
||||
out[gatewayProviderID] = map[string]any{
|
||||
out[GatewayProviderKey(p.Gateway.URL)] = map[string]any{
|
||||
"type": "openai-compat",
|
||||
"base_url": p.Gateway.URL,
|
||||
"api_key": p.Gateway.Token,
|
||||
@@ -106,7 +128,7 @@ func CrushRoleModels(p *canonical.Profile) map[string]any {
|
||||
}
|
||||
sel := map[string]any{
|
||||
"model": p.Gateway.DefaultModel,
|
||||
"provider": gatewayProviderID,
|
||||
"provider": GatewayProviderKey(p.Gateway.URL),
|
||||
}
|
||||
return map[string]any{
|
||||
"large": sel,
|
||||
@@ -143,7 +165,7 @@ func ProvidersAsMap(p *canonical.Profile) map[string]any {
|
||||
if len(models) > 0 {
|
||||
entry["models"] = models
|
||||
}
|
||||
out[gatewayProviderID] = entry
|
||||
out[GatewayProviderKey(p.Gateway.URL)] = entry
|
||||
}
|
||||
for _, up := range p.Upstreams {
|
||||
entry := map[string]any{
|
||||
@@ -169,7 +191,7 @@ func KiloModelString(p *canonical.Profile) string {
|
||||
if p.Gateway.DefaultModel == "" {
|
||||
return ""
|
||||
}
|
||||
return gatewayProviderID + "/" + p.Gateway.DefaultModel
|
||||
return GatewayProviderKey(p.Gateway.URL) + "/" + p.Gateway.DefaultModel
|
||||
}
|
||||
|
||||
// ProvidersAsCagentMap returns the providers block for cagent:
|
||||
@@ -179,7 +201,7 @@ func ProvidersAsCagentMap(p *canonical.Profile) map[string]any {
|
||||
if p.Gateway.URL == "" {
|
||||
return out
|
||||
}
|
||||
out[gatewayProviderID] = map[string]any{
|
||||
out[GatewayProviderKey(p.Gateway.URL)] = map[string]any{
|
||||
"base_url": p.Gateway.URL,
|
||||
"api_key": p.Gateway.Token,
|
||||
"provider": "openai",
|
||||
@@ -227,7 +249,7 @@ func GooseCustomProviderFile(p *canonical.Profile) ([]byte, string) {
|
||||
if p.Gateway.URL == "" {
|
||||
return nil, ""
|
||||
}
|
||||
providerID := gatewayProviderID
|
||||
providerID := GatewayProviderKey(p.Gateway.URL)
|
||||
providerName := "custom_" + providerID
|
||||
|
||||
models := make([]map[string]any, 0)
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestBuildProvidersGatewayFirst(t *testing.T) {
|
||||
}
|
||||
out := BuildProviders(p)
|
||||
require.Len(t, out, 2)
|
||||
assert.Equal(t, "harness-sync-gateway", out[0]["id"])
|
||||
assert.Equal(t, "hs-gw", out[0]["id"])
|
||||
assert.Equal(t, "anthropic", out[1]["id"])
|
||||
assert.NotNil(t, out[0]["models"])
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package common
|
||||
|
||||
import "maps"
|
||||
|
||||
// AbsorbDuplicateProviders scans provMap for entries (other than ourKey) whose
|
||||
// options.baseURL matches gatewayURL and absorbs their models into our entry.
|
||||
//
|
||||
// Merge strategy:
|
||||
// - Models only in the duplicate: added to our provider.
|
||||
// - Models only in our canonical entry: kept as-is.
|
||||
// - Models in both: the DUPLICATE's entry wins (preserves user-crafted display
|
||||
// names and custom limits that are often more descriptive than the profile
|
||||
// alias).
|
||||
//
|
||||
// Absorbed entries are deleted from the returned map. Returns provMap unchanged
|
||||
// when gatewayURL is empty or our entry is absent.
|
||||
func AbsorbDuplicateProviders(provMap map[string]any, ourKey, gatewayURL string) map[string]any {
|
||||
if gatewayURL == "" || provMap == nil {
|
||||
return provMap
|
||||
}
|
||||
ourEntry, ok := provMap[ourKey].(map[string]any)
|
||||
if !ok {
|
||||
return provMap
|
||||
}
|
||||
|
||||
// Clone top-level so we don't mutate the caller's map.
|
||||
result := make(map[string]any, len(provMap))
|
||||
maps.Copy(result, provMap)
|
||||
|
||||
ourModels, _ := ourEntry["models"].(map[string]any)
|
||||
if ourModels == nil {
|
||||
ourModels = map[string]any{}
|
||||
}
|
||||
|
||||
// merged starts with our canonical models; duplicates may override per-model.
|
||||
merged := make(map[string]any, len(ourModels))
|
||||
maps.Copy(merged, ourModels)
|
||||
|
||||
for k, v := range result {
|
||||
if k == ourKey {
|
||||
continue
|
||||
}
|
||||
entry, ok := v.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
opts, _ := entry["options"].(map[string]any)
|
||||
if opts == nil {
|
||||
continue
|
||||
}
|
||||
if opts["baseURL"] != gatewayURL {
|
||||
continue
|
||||
}
|
||||
// Same gateway URL — absorb and delete.
|
||||
if dupModels, ok := entry["models"].(map[string]any); ok {
|
||||
// Duplicate (user-crafted) wins for overlapping IDs.
|
||||
maps.Copy(merged, dupModels)
|
||||
}
|
||||
delete(result, k)
|
||||
}
|
||||
|
||||
updated := make(map[string]any, len(ourEntry))
|
||||
maps.Copy(updated, ourEntry)
|
||||
if len(merged) > 0 {
|
||||
updated["models"] = merged
|
||||
}
|
||||
result[ourKey] = updated
|
||||
return result
|
||||
}
|
||||
@@ -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