Skip to content

Module augmentation ​

Many xsAI types have a slot for provider-specific data, such as providerOptions on a request or providerMetadata on a result. Each slot is an empty interface that packages fill through TypeScript module augmentation. The adapters use it, and you can use it to type the options and metadata of your own model.

How the adapters use it ​

@xsai/text-responses declares this, in its own source:

ts
import type { ResponsesProviderOptions } from '@xsai/text-responses'

declare module '@xsai/text' {
  interface LanguageModelProviderOptions {
    
responses
?: ResponsesProviderOptions
} }

TypeScript merges the declaration with the empty LanguageModelProviderOptions in @xsai/text. After you import @xsai/text-responses, providerOptions: { responses: { store: true } } type-checks. A package for another protocol adds its own key, so the keys never overlap.

Add options for a model ​

This example adds a region option for a model called example. The model reads it from providerOptions.example.

ts
declare module '@xsai/text' {
  interface LanguageModelProviderOptions {
    
example
?: {
region
?: 'eu' | 'us' }
} } const
example
= ():
LanguageModel
=> ({
providerOptions
}) =>
textStream
(`Served from ${
providerOptions
?.
example
?.
region
?? 'us'}.`)
const {
text
} = await
generateText
(
example
(), {
input
: 'Hi.',
providerOptions
: {
example
: {
region
: 'eu' } },
})
console
.
log
(
text
)

The compiler now checks the namespace. A misspelled field is an error:

ts
import type { 
LanguageModel
} from '@xsai/text'
declare const
model
:
LanguageModel
declare module '@xsai/text' { interface LanguageModelProviderOptions {
example
?: {
region
?: 'eu' | 'us' }
} } await
model
({
input
: 'Hi.',
providerOptions
: {
example
: { regoin: 'eu' } } })
Object literal may only specify known properties, but 'regoin' does not exist in type '{ region?: "eu" | "us" | undefined; }'. Did you mean to write 'region'?

Add metadata to a result ​

Metadata slots work the same way. This example adds citations to every text Part.

ts
declare module '@xsai/text' {
  interface PartProviderMetadata {
    
example
?: {
citations
?: string[] }
} } const {
message
} = await
generateText
(
model
, {
input
: 'Hi.' })
if (typeof
message
.
content
!== 'string') {
for (const
part
of
message
.
content
) {
if (
part
.
type
=== 'text')
console
.
log
(
part
.
providerMetadata
?.
example
?.
citations
)
} }

Add an error code ​

XSAIErrorCauseMap in @xsai/shared maps each error code to the type of its cause. undefined forbids a cause, unknown makes it optional, and any other type requires it.

ts
import { 
XSAIError
} from '@xsai/shared'
declare module '@xsai/shared' { interface XSAIErrorCauseMap { 'quota-exceeded': {
limit
: number }
} } throw new
XSAIError
('quota-exceeded', 'The daily quota is used up.', {
cause
: {
limit
: 100 } })

Extension points ​

PackageInterfaceSlot
@xsai/textLanguageModelProviderOptionsproviderOptions of a text request.
@xsai/textPartProviderMetadataproviderMetadata of a Part.
@xsai/textMessageProviderMetadataproviderMetadata of a message.
@xsai/audioSpeechModelProviderOptionsproviderOptions of a speech request.
@xsai/audioTranscriptionModelProviderOptionsproviderOptions of a transcription request.
@xsai/audioTranscriptionSegmentProviderMetadata, TranscriptionWordProviderMetadataproviderMetadata of a segment or a word.
@xsai/decideDecisionModelProviderOptionsproviderOptions of a decision request.
@xsai/embedEmbeddingModelProviderOptionsproviderOptions of an embedding request.
@xsai/imageImageModelProviderOptions, ImageModelResultProviderMetadataproviderOptions of a request, and providerMetadata of a result.
@xsai/modelModelCatalogProviderOptions, ModelCatalogEntryProviderMetadataproviderOptions of a request, and providerMetadata of an entry.
@xsai/sharedXSAIErrorCauseMapError codes and their causes.

Rules ​

  1. Augment the package that declares the interface, as in the table.
  2. The file must be a module, so it needs at least one import or export. Without one, declare module replaces the package types instead of extending them.
  3. Make each property optional, so code that does not use your model still compiles.
  4. Use a key that is unique to your package, such as the name of its factory. Two packages that declare the same key with different types make the build fail.
  5. An augmentation applies to the whole project, not to one model. Every model type accepts the key, and a model ignores keys that it does not read.
  6. If you publish a package, export the augmentation from its entry point, so it applies when a user imports your package.

Contributors

Changelog