Why: F13 - the squad factory read the default account with an empty project, pairing the run user's model name with another account's endpoint and credentials; direct and squad paths disagreed on reasoning split and route contract. Evidence: app/runtime/squadadapter tests pass (verify-u10b.json); run-scoped snapshot, project override, profile override and fail-closed cases added with synthetic keys. Impact: squad execution now uses the same effective-config rules as the direct path; default setups unchanged. Refs: feature/agent-stack-audit-remediation-b1 U10
63 lines
2.4 KiB
Go
63 lines
2.4 KiB
Go
package runtime
|
|
|
|
import (
|
|
"strings"
|
|
|
|
agentllm "agent-native/runtime/llm"
|
|
core "agent-system-core"
|
|
)
|
|
|
|
// 运行配置解析的窄端口。直连 RunCtx 与小队 squadadapter.Ctx 共用同一组
|
|
// 可选接口:任一执行面都能从「本次运行」拿到同一份生效快照,绝不按默认账号
|
|
// 另起一套解析(KTD3)。
|
|
|
|
type runSettingsPort interface{ Settings() core.EffectiveSettings }
|
|
type runProfilePort interface{ Profile() core.AgentProfile }
|
|
type runScopePort interface{ Run() core.Run }
|
|
|
|
// RunEffectiveSettings 返回本次运行生效的配置快照(Settings 端口已由宿主按
|
|
// run.Owner + run.ProjectID 合并 defaults→system→user→project)。
|
|
// ok=false 表示宿主没有该端口或快照为空;调用方自行决定回落策略,
|
|
// 不得在有快照时回落到别的属主。
|
|
func RunEffectiveSettings(rc any) (core.EffectiveSettings, bool) {
|
|
port, ok := rc.(runSettingsPort)
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
eff := port.Settings()
|
|
if len(eff) == 0 {
|
|
return nil, false
|
|
}
|
|
return eff, true
|
|
}
|
|
|
|
// RunProfile 返回本次运行的画像:宿主实现 Profile 端口时直接取;否则按
|
|
// run.Owner 合成零值画像——Owner 决定密钥层归属(overlayKey),缺失时
|
|
// 绝不能退化成默认账号,否则会用别人的密钥。
|
|
func RunProfile(rc any) core.AgentProfile {
|
|
var prof core.AgentProfile
|
|
if port, ok := rc.(runProfilePort); ok {
|
|
prof = port.Profile()
|
|
}
|
|
if strings.TrimSpace(prof.Owner) == "" {
|
|
if scope, ok := rc.(runScopePort); ok {
|
|
prof.Owner = scope.Run().Owner
|
|
}
|
|
}
|
|
return prof
|
|
}
|
|
|
|
// RunChatRoute 解析本次运行的主对话路由(画像 ModelRoute > 当前用户/项目设置层)。
|
|
// SquadRunner 用它给小队入口定模型,使小队与直连共用同一条解析规则。
|
|
func RunChatRoute(rc RunCtx) ResolvedModel { return ResolveChatModel(rc.Profile(), rc.Settings()) }
|
|
|
|
// OpenRouteClient 按一次解析出的路由构造 OpenAI 兼容客户端:端点与密钥取自
|
|
// 本路由自己的解析结果,MiniMax 系模型的 reasoning split 与受限 EOF route
|
|
// contract 都只看本路由的 model+base。直连、小队与其他执行面必须共用它,
|
|
// 避免同一个 run 里出现两套 per-route 规则(分流的思考、EOF 放行会不一致)。
|
|
func OpenRouteClient(route ResolvedModel, eff core.EffectiveSettings) *agentllm.Client {
|
|
c := openLLMClient(route.Base, route.Key, route.Model)
|
|
c.RouteContract = routeContractFor(eff, route)
|
|
return c
|
|
}
|