Add environment variable resolution for agent settings
- Introduce `resolveEnvStrings` function to handle `$ENV.XXX` references in the agent's DSL settings, ensuring that environment variables are correctly substituted in system and uses fields. - Enhance `Load` function to call `resolveEnvStrings` during the loading process, improving the configuration handling for agents. - Add comprehensive unit tests for `resolveEnvStrings` to validate the correct resolution of environment variables across various fields and scenarios, including handling of undefined variables and plain strings. - Ensure that the implementation maintains existing functionality while enhancing flexibility for environment-based configurations.
This commit is contained in:
parent
a38d115465
commit
a28d61ed78
2 changed files with 144 additions and 0 deletions
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/yaoapp/gou/application"
|
"github.com/yaoapp/gou/application"
|
||||||
"github.com/yaoapp/gou/connector"
|
"github.com/yaoapp/gou/connector"
|
||||||
|
"github.com/yaoapp/gou/helper"
|
||||||
"github.com/yaoapp/yao/agent/assistant"
|
"github.com/yaoapp/yao/agent/assistant"
|
||||||
"github.com/yaoapp/yao/agent/context"
|
"github.com/yaoapp/yao/agent/context"
|
||||||
"github.com/yaoapp/yao/agent/i18n"
|
"github.com/yaoapp/yao/agent/i18n"
|
||||||
|
|
@ -46,6 +47,9 @@ func Load(cfg config.Config) error {
|
||||||
setting.StoreSetting.MaxSize = 20 // default is 20
|
setting.StoreSetting.MaxSize = 20 // default is 20
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve $ENV.XXX references in system and uses fields
|
||||||
|
resolveEnvStrings(&setting)
|
||||||
|
|
||||||
// Default Assistant, Agent is the developer name, Mohe is the brand name of the assistant
|
// Default Assistant, Agent is the developer name, Mohe is the brand name of the assistant
|
||||||
if setting.Uses == nil {
|
if setting.Uses == nil {
|
||||||
setting.Uses = &types.Uses{Default: "mohe"} // Agent is the developer name, Mohe is the brand name of the assistant
|
setting.Uses = &types.Uses{Default: "mohe"} // Agent is the developer name, Mohe is the brand name of the assistant
|
||||||
|
|
@ -444,3 +448,36 @@ func defaultAssistant() (*assistant.Assistant, error) {
|
||||||
}
|
}
|
||||||
return assistant.Get(agentDSL.Uses.Default)
|
return assistant.Get(agentDSL.Uses.Default)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolveEnvStrings resolves $ENV.XXX references in agent.yml string fields.
|
||||||
|
// agent.yml is parsed via yaml.Unmarshal which does not handle $ENV substitution,
|
||||||
|
// unlike connector files which call helper.EnvString explicitly during Register.
|
||||||
|
func resolveEnvStrings(setting *types.DSL) {
|
||||||
|
if setting.System != nil {
|
||||||
|
setting.System.Default = helper.EnvString(setting.System.Default)
|
||||||
|
setting.System.Keyword = helper.EnvString(setting.System.Keyword)
|
||||||
|
setting.System.QueryDSL = helper.EnvString(setting.System.QueryDSL)
|
||||||
|
setting.System.Title = helper.EnvString(setting.System.Title)
|
||||||
|
setting.System.Prompt = helper.EnvString(setting.System.Prompt)
|
||||||
|
setting.System.RobotPrompt = helper.EnvString(setting.System.RobotPrompt)
|
||||||
|
setting.System.NeedSearch = helper.EnvString(setting.System.NeedSearch)
|
||||||
|
setting.System.Entity = helper.EnvString(setting.System.Entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
if setting.Uses != nil {
|
||||||
|
setting.Uses.Default = helper.EnvString(setting.Uses.Default)
|
||||||
|
setting.Uses.Title = helper.EnvString(setting.Uses.Title)
|
||||||
|
setting.Uses.Prompt = helper.EnvString(setting.Uses.Prompt)
|
||||||
|
setting.Uses.RobotPrompt = helper.EnvString(setting.Uses.RobotPrompt)
|
||||||
|
setting.Uses.Vision = helper.EnvString(setting.Uses.Vision)
|
||||||
|
setting.Uses.Audio = helper.EnvString(setting.Uses.Audio)
|
||||||
|
setting.Uses.Search = helper.EnvString(setting.Uses.Search)
|
||||||
|
setting.Uses.Fetch = helper.EnvString(setting.Uses.Fetch)
|
||||||
|
setting.Uses.Web = helper.EnvString(setting.Uses.Web)
|
||||||
|
setting.Uses.Keyword = helper.EnvString(setting.Uses.Keyword)
|
||||||
|
setting.Uses.QueryDSL = helper.EnvString(setting.Uses.QueryDSL)
|
||||||
|
setting.Uses.Rerank = helper.EnvString(setting.Uses.Rerank)
|
||||||
|
}
|
||||||
|
|
||||||
|
setting.Cache = helper.EnvString(setting.Cache)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/yaoapp/yao/agent/assistant"
|
"github.com/yaoapp/yao/agent/assistant"
|
||||||
|
"github.com/yaoapp/yao/agent/types"
|
||||||
"github.com/yaoapp/yao/config"
|
"github.com/yaoapp/yao/config"
|
||||||
"github.com/yaoapp/yao/test"
|
"github.com/yaoapp/yao/test"
|
||||||
)
|
)
|
||||||
|
|
@ -211,6 +212,112 @@ func TestGetGlobalPromptsWithDisableFlag(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolveEnvStrings(t *testing.T) {
|
||||||
|
t.Setenv("TEST_CONNECTOR", "openai.gpt-5")
|
||||||
|
t.Setenv("TEST_ASSISTANT", "my-assistant")
|
||||||
|
t.Setenv("TEST_CACHE", "my-cache")
|
||||||
|
|
||||||
|
t.Run("SystemFields", func(t *testing.T) {
|
||||||
|
setting := &types.DSL{
|
||||||
|
System: &types.System{
|
||||||
|
Default: "$ENV.TEST_CONNECTOR",
|
||||||
|
Keyword: "$ENV.TEST_CONNECTOR",
|
||||||
|
QueryDSL: "$ENV.TEST_CONNECTOR",
|
||||||
|
Title: "$ENV.TEST_CONNECTOR",
|
||||||
|
Prompt: "$ENV.TEST_CONNECTOR",
|
||||||
|
RobotPrompt: "$ENV.TEST_CONNECTOR",
|
||||||
|
NeedSearch: "$ENV.TEST_CONNECTOR",
|
||||||
|
Entity: "$ENV.TEST_CONNECTOR",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resolveEnvStrings(setting)
|
||||||
|
|
||||||
|
assert.Equal(t, "openai.gpt-5", setting.System.Default)
|
||||||
|
assert.Equal(t, "openai.gpt-5", setting.System.Keyword)
|
||||||
|
assert.Equal(t, "openai.gpt-5", setting.System.QueryDSL)
|
||||||
|
assert.Equal(t, "openai.gpt-5", setting.System.Title)
|
||||||
|
assert.Equal(t, "openai.gpt-5", setting.System.Prompt)
|
||||||
|
assert.Equal(t, "openai.gpt-5", setting.System.RobotPrompt)
|
||||||
|
assert.Equal(t, "openai.gpt-5", setting.System.NeedSearch)
|
||||||
|
assert.Equal(t, "openai.gpt-5", setting.System.Entity)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("UsesFields", func(t *testing.T) {
|
||||||
|
setting := &types.DSL{
|
||||||
|
Uses: &types.Uses{
|
||||||
|
Default: "$ENV.TEST_ASSISTANT",
|
||||||
|
Title: "$ENV.TEST_ASSISTANT",
|
||||||
|
Prompt: "$ENV.TEST_ASSISTANT",
|
||||||
|
RobotPrompt: "$ENV.TEST_ASSISTANT",
|
||||||
|
Vision: "$ENV.TEST_ASSISTANT",
|
||||||
|
Audio: "$ENV.TEST_ASSISTANT",
|
||||||
|
Search: "$ENV.TEST_ASSISTANT",
|
||||||
|
Fetch: "$ENV.TEST_ASSISTANT",
|
||||||
|
Web: "$ENV.TEST_ASSISTANT",
|
||||||
|
Keyword: "$ENV.TEST_ASSISTANT",
|
||||||
|
QueryDSL: "$ENV.TEST_ASSISTANT",
|
||||||
|
Rerank: "$ENV.TEST_ASSISTANT",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resolveEnvStrings(setting)
|
||||||
|
|
||||||
|
assert.Equal(t, "my-assistant", setting.Uses.Default)
|
||||||
|
assert.Equal(t, "my-assistant", setting.Uses.Title)
|
||||||
|
assert.Equal(t, "my-assistant", setting.Uses.Prompt)
|
||||||
|
assert.Equal(t, "my-assistant", setting.Uses.RobotPrompt)
|
||||||
|
assert.Equal(t, "my-assistant", setting.Uses.Vision)
|
||||||
|
assert.Equal(t, "my-assistant", setting.Uses.Audio)
|
||||||
|
assert.Equal(t, "my-assistant", setting.Uses.Search)
|
||||||
|
assert.Equal(t, "my-assistant", setting.Uses.Fetch)
|
||||||
|
assert.Equal(t, "my-assistant", setting.Uses.Web)
|
||||||
|
assert.Equal(t, "my-assistant", setting.Uses.Keyword)
|
||||||
|
assert.Equal(t, "my-assistant", setting.Uses.QueryDSL)
|
||||||
|
assert.Equal(t, "my-assistant", setting.Uses.Rerank)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("CacheField", func(t *testing.T) {
|
||||||
|
setting := &types.DSL{Cache: "$ENV.TEST_CACHE"}
|
||||||
|
resolveEnvStrings(setting)
|
||||||
|
assert.Equal(t, "my-cache", setting.Cache)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("PlainStringsUnchanged", func(t *testing.T) {
|
||||||
|
setting := &types.DSL{
|
||||||
|
Cache: "plain-cache",
|
||||||
|
System: &types.System{
|
||||||
|
Default: "openai.gpt-5",
|
||||||
|
},
|
||||||
|
Uses: &types.Uses{
|
||||||
|
Default: "mohe",
|
||||||
|
Title: "__yao.title",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resolveEnvStrings(setting)
|
||||||
|
|
||||||
|
assert.Equal(t, "plain-cache", setting.Cache)
|
||||||
|
assert.Equal(t, "openai.gpt-5", setting.System.Default)
|
||||||
|
assert.Equal(t, "mohe", setting.Uses.Default)
|
||||||
|
assert.Equal(t, "__yao.title", setting.Uses.Title)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("NilSystemAndUses", func(t *testing.T) {
|
||||||
|
setting := &types.DSL{Cache: "test"}
|
||||||
|
assert.NotPanics(t, func() {
|
||||||
|
resolveEnvStrings(setting)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("UndefinedEnvReturnsEmpty", func(t *testing.T) {
|
||||||
|
setting := &types.DSL{
|
||||||
|
System: &types.System{
|
||||||
|
Default: "$ENV.UNDEFINED_VAR_12345",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resolveEnvStrings(setting)
|
||||||
|
assert.Equal(t, "", setting.System.Default)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestGlobalPromptsContent(t *testing.T) {
|
func TestGlobalPromptsContent(t *testing.T) {
|
||||||
prepare(t)
|
prepare(t)
|
||||||
defer test.Clean()
|
defer test.Clean()
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue