Files
weichen 80faf3edbd Resolve squad routes from the run owner and project
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
2026-10-21 16:15:03 +00:00

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
}