feat(auth): support embedded profile in model ID
- parseModel extracts authProfile from providerID if contains ':' - Profile embedded in model string has highest priority - Precedence: embedded > config authProfile > env var > default - Auth.get supports direct lookup of provider:profile keys - Agent.Info model supports optional authProfile fieldpull/21353/head
parent
153198061d
commit
4a17c7e143
|
|
@ -39,6 +39,7 @@ export namespace Agent {
|
|||
.object({
|
||||
modelID: ModelID.zod,
|
||||
providerID: ProviderID.zod,
|
||||
authProfile: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
variant: z.string().optional(),
|
||||
|
|
|
|||
|
|
@ -73,7 +73,11 @@ export namespace Auth {
|
|||
if (providerID in allData) return allData[providerID]
|
||||
const withSlash = providerID.endsWith("/") ? providerID.slice(0, -1) : providerID + "/"
|
||||
if (withSlash in allData) return allData[withSlash]
|
||||
// Multi-profile support: try normalized key
|
||||
// Multi-profile support: if key has embedded profile (provider:profile), try direct
|
||||
if (providerID.includes(":")) {
|
||||
if (providerID in allData) return allData[providerID]
|
||||
}
|
||||
// Multi-profile support: try normalized key with :default
|
||||
const withProfile = normalizeKey(providerID)
|
||||
if (withProfile in allData) return allData[withProfile]
|
||||
const bare = providerID.replace(/\/+$/, "")
|
||||
|
|
|
|||
|
|
@ -820,6 +820,7 @@ export namespace Provider {
|
|||
.object({
|
||||
id: ModelID.zod,
|
||||
providerID: ProviderID.zod,
|
||||
authProfile: z.string().optional(),
|
||||
api: z.object({
|
||||
id: z.string(),
|
||||
url: z.string(),
|
||||
|
|
@ -1030,10 +1031,12 @@ export namespace Provider {
|
|||
[providerID: string]: CustomDiscoverModels
|
||||
} = {}
|
||||
// resolveProfile returns the auth profile for a provider with precedence:
|
||||
// 1. opencode.json → provider[providerID].options.authProfile
|
||||
// 2. {PROVIDER_ID_UPPERCASE}_AUTH_PROFILE env var (dots replaced by underscores)
|
||||
// 3. "default"
|
||||
function resolveProfile(providerID: string): string {
|
||||
// 1. Embedded profile from model string (e.g., "provider:profile/model") - highest priority
|
||||
// 2. opencode.json → provider[providerID].options.authProfile
|
||||
// 3. {PROVIDER_ID_UPPERCASE}_AUTH_PROFILE env var (dots replaced by underscores)
|
||||
// 4. "default"
|
||||
function resolveProfile(providerID: string, profileOverride?: string): string {
|
||||
if (profileOverride) return profileOverride
|
||||
const cfg = providers[providerID as ProviderID]
|
||||
if (cfg?.options?.authProfile) return cfg.options.authProfile
|
||||
const envKey = `${providerID.toUpperCase().replace(/\./g, "_")}_AUTH_PROFILE`
|
||||
|
|
@ -1043,15 +1046,13 @@ export namespace Provider {
|
|||
}
|
||||
|
||||
const dep = {
|
||||
auth: (id: string) => {
|
||||
const profile = resolveProfile(id)
|
||||
auth: (id: string, profileOverride?: string) => {
|
||||
const profile = resolveProfile(id, profileOverride)
|
||||
const key = profile !== "default" ? `${id.replace(/\/+$/, "")}:${profile}` : id
|
||||
if (profile !== "default") {
|
||||
log.info(`Using auth profile: ${profile}`)
|
||||
const key = `${id.replace(/\/+$/, "")}:${profile}`
|
||||
return auth.get(key).pipe(Effect.orDie)
|
||||
}
|
||||
// For default profile, pass original ID to preserve backward compat
|
||||
return auth.get(id).pipe(Effect.orDie)
|
||||
return auth.get(key).pipe(Effect.orDie)
|
||||
},
|
||||
config: () => config.get(),
|
||||
}
|
||||
|
|
@ -1705,10 +1706,19 @@ export namespace Provider {
|
|||
)
|
||||
}
|
||||
|
||||
export function parseModel(model: string) {
|
||||
const [providerID, ...rest] = model.split("/")
|
||||
export function parseModel(model: string): { providerID: ProviderID; modelID: ModelID; authProfile?: string } {
|
||||
const [first, ...rest] = model.split("/")
|
||||
// Check if providerID contains a profile (e.g., "provider:profile")
|
||||
if (first.includes(":")) {
|
||||
const [providerID, authProfile] = first.split(":")
|
||||
return {
|
||||
providerID: ProviderID.make(providerID),
|
||||
modelID: ModelID.make(rest.join("/")),
|
||||
authProfile,
|
||||
}
|
||||
}
|
||||
return {
|
||||
providerID: ProviderID.make(providerID),
|
||||
providerID: ProviderID.make(first),
|
||||
modelID: ModelID.make(rest.join("/")),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue