diff --git a/docs/integration/heicode-am-contract.md b/docs/integration/heicode-am-contract.md index ce992d82..d092bae7 100644 --- a/docs/integration/heicode-am-contract.md +++ b/docs/integration/heicode-am-contract.md @@ -79,7 +79,7 @@ HM 把用户绑定的资源按**资源类型 + provider**解成固定 env 名( | database | postgres | `POSTGRES_HOST` `POSTGRES_PORT` `POSTGRES_DATABASE` `POSTGRES_USER` · `POSTGRES_PASSWORD`(密) | | storage | azure blob | `AZURE_BLOB_ACCOUNT_NAME` `AZURE_BLOB_CONTAINER` · `AZURE_BLOB_ACCOUNT_KEY`(密) | -另外随启动注入(模型网关):`OPENAI_BASE_URL`(= HM `/v1`)、`MODEL_NAME`。**`OPENAI_API_KEY` 不在启动时注入** —— 由客户端在 A2A 请求里带 `api_key`(你们文档 §7);后续若走 V2 解密路径再调整。 +另外随启动注入(模型网关):`OPENAI_BASE_URL`(= HM `/v1`)、`MODEL_NAME`(**默认 `gpt-5.4`**,可用 env `AGENT_RUNTIME_DEFAULT_MODEL` 覆盖;模板 frontmatter 里的 `opus/sonnet` 只是角色提示,**不会**当作网关模型传)。**`OPENAI_API_KEY` 不在启动时注入** —— 由客户端在 A2A 请求里带 `api_key`(你们文档 §7);后续若走 V2 解密路径再调整。 - 缺失的可选字段不会出现在 env 里(agent 自行容错)。 - 同类型资源 HM 限制只挂一个(避免 env 名冲突)。 diff --git a/heicode/controller/agent_template_runtime.go b/heicode/controller/agent_template_runtime.go index 13dc1669..4ace5ec0 100644 --- a/heicode/controller/agent_template_runtime.go +++ b/heicode/controller/agent_template_runtime.go @@ -148,9 +148,17 @@ func amStartTemplateAgent(ctx context.Context, args amStartArgs) (amStartResult, if strings.TrimSpace(args.AgentDefinition) != "" { env["AGENT_INSTRUCTION_TEXT"] = args.AgentDefinition } - if args.Model != "" { - env["MODEL_NAME"] = args.Model + // MODEL_NAME must be a real model on the HM gateway. Template frontmatter + // carries a Claude-style tier hint (opus/sonnet/haiku) which is NOT a gateway + // model — fall back to the gateway default (gpt-5.4, env-overridable). + modelName := common.GetEnvOrDefaultString("AGENT_RUNTIME_DEFAULT_MODEL", "gpt-5.4") + switch strings.ToLower(strings.TrimSpace(args.Model)) { + case "", "opus", "sonnet", "haiku", "claude": + // keep the gateway default + default: + modelName = strings.TrimSpace(args.Model) } + env["MODEL_NAME"] = modelName env["OPENAI_BASE_URL"] = publicV1BaseURL() // NOTE: OPENAI_API_KEY is intentionally NOT injected here — the desktop // client passes its own api_key per A2A request (CODING_A2A §7). A future diff --git a/heicode/controller/agent_template_test.go b/heicode/controller/agent_template_test.go index f56c0f16..837717e9 100644 --- a/heicode/controller/agent_template_test.go +++ b/heicode/controller/agent_template_test.go @@ -169,12 +169,33 @@ func TestAMStartTemplateAgent_RoundTrip(t *testing.T) { require.Contains(t, gotBody, "AGENT_ROLE_NAME") require.Contains(t, gotBody, "OPENAI_BASE_URL") require.Contains(t, gotBody, "manager_deployment_id") + require.Contains(t, gotBody, `"MODEL_NAME":"gpt-5.4"`) // real gateway model // response parsed require.Equal(t, "ns-1", res.RuntimeID) require.Equal(t, "https://abc.agents.example", res.Subdomain) require.Equal(t, "running", res.Status) // defaulted } +func TestAMStartTemplateAgent_ModelDefaultsToGateway(t *testing.T) { + var gotBody string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + b, _ := io.ReadAll(r.Body) + gotBody = string(b) + _, _ = w.Write([]byte(`{"success":true,"data":{"namespace":"ns"}}`)) + })) + defer srv.Close() + t.Setenv("AGENT_RUNTIME_BASE_URL", srv.URL) + + // template frontmatter model "opus" is a Claude tier, not a gateway model. + _, err := amStartTemplateAgent(context.Background(), amStartArgs{ + ManagerDeploymentID: "dep_1", UserID: "22", TemplateKey: "architect", + AgentDefinition: "x", Model: "opus", + }) + require.NoError(t, err) + require.Contains(t, gotBody, `"MODEL_NAME":"gpt-5.4"`) // defaulted, not "opus" + require.NotContains(t, gotBody, `"MODEL_NAME":"opus"`) +} + func TestAMGetAgentStatus_RoundTrip(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.Equal(t, "/agents/rt-1", r.URL.Path)