UNPKG

@tanstack/ai

Version:

Type-safe TypeScript AI SDK for streaming chat, tool calling, agents, structured outputs, and multimodal generation.

94 lines (89 loc) • 2.73 kB
import { providerKeys } from './env-keys' import { resolveProviderId } from './providers' import type { Credential, CredentialScope, CredentialStore, } from './credentials' import type { ProviderKeys } from './keyed' /** * The tenant scope of a user scope: the same tenant, no user. `undefined` for * a scope with no user or no tenant, so a user without a tenant never reads * the bucket shared by every scope with neither. */ export function tenantScopeOf({ userId, tenantId, }: CredentialScope): CredentialScope | undefined { return userId === undefined || tenantId === undefined ? undefined : { tenantId } } /** * The credential `id` of `scope`: the user's own first, then the tenant's * (saved without a `userId`). A save or a delete changes the user's own only. */ export async function readCredential( store: CredentialStore, scope: CredentialScope, id: string, ): Promise<Credential | null> { return (await readCredentialEntry(store, scope, id))?.credential ?? null } /** * Like {@link readCredential}, but also gives the scope where the credential * was found (`scope` itself, or its tenant scope) and its `revision` when the * store has `getVersioned`. Write a refreshed credential back to that scope, * with `setIf` and that revision. */ export async function readCredentialEntry( store: CredentialStore, scope: CredentialScope, id: string, ): Promise<{ credential: Credential scope: CredentialScope revision?: string } | null> { const tenant = tenantScopeOf(scope) for (const candidate of tenant ? [scope, tenant] : [scope]) { if (store.getVersioned) { const found = await store.getVersioned({ scope: candidate, id }) if (found) return { ...found, scope: candidate } } else { const credential = await store.get({ scope: candidate, id }) if (credential) return { credential, scope: candidate } } } return null } /** * Provider keys saved on the server: the `api_key` credential stored under * the provider id for `scope` (the user's, else the tenant's), else the * provider's `env` names. * * @example * ```ts * const gpt = keyedAdapter(openaiByok, (key) => * createOpenaiChat('gpt-6.1-sol', key), * ) * const keys = storedProviderKeys(credentials, { userId, tenantId }) * chat({ * adapter: await keys.adapter(gpt), * subagents: { agents, keys: keys.get }, * }) * ``` */ export function storedProviderKeys( store: CredentialStore, scope: CredentialScope, ): ProviderKeys { return providerKeys(async (provider) => { const saved = await readCredential( store, scope, resolveProviderId(provider), ) return saved?.type === 'api_key' ? saved.value : null }) }