@tanstack/ai
Version:
Type-safe TypeScript AI SDK for streaming chat, tool calling, agents, structured outputs, and multimodal generation.
60 lines (53 loc) • 1.96 kB
text/typescript
import type { TanStackMessageMetadata, TanStackRunMetadata } from '../types'
export type MetadataRecord = Record<string, any>
/** `metadata.tanstack` on a message or run event. Internal getter return type. */
export type TanStackMetadata = TanStackMessageMetadata & TanStackRunMetadata
function isPlainRecord(value: unknown): value is MetadataRecord {
return value != null && typeof value === 'object' && !Array.isArray(value)
}
export function mergeMetadata(
current: MetadataRecord | undefined,
incoming: MetadataRecord | null | undefined,
): MetadataRecord | undefined {
if (incoming == null) return current
if (current == null) return { ...incoming }
const merged: MetadataRecord = { ...current, ...incoming }
if (isPlainRecord(current.tanstack) && isPlainRecord(incoming.tanstack)) {
merged.tanstack = { ...current.tanstack, ...incoming.tanstack }
}
return merged
}
/** Read `metadata.tanstack`. Not part of the public SDK surface. */
export function tanstackMetadata(
value: { metadata?: MetadataRecord | null } | MetadataRecord | undefined,
): TanStackMetadata | undefined {
if (value == null) return undefined
const nested = 'metadata' in value ? value.metadata : undefined
const metadata =
nested != null && typeof nested === 'object' && !Array.isArray(nested)
? nested
: (value as MetadataRecord)
const tanstack = metadata.tanstack
if (
tanstack == null ||
typeof tanstack !== 'object' ||
Array.isArray(tanstack)
) {
return undefined
}
return tanstack as TanStackMetadata
}
export function withTanstackMetadata<T>(
value: T & { metadata?: MetadataRecord | null },
tanstack: MetadataRecord,
): Omit<T, 'metadata'> & { metadata: MetadataRecord } {
const current = value.metadata
const currentTanstack = tanstackMetadata(value) ?? {}
return {
...value,
metadata: {
...(current ?? {}),
tanstack: { ...currentTanstack, ...tanstack },
},
}
}