Documentation Index
Fetch the complete documentation index at: https://mintlify.com/janhq/jan/llms.txt
Use this file to discover all available pages before exploring further.
The @janhq/core package provides all the APIs, types, and interfaces needed to build Jan extensions.
Installation
Import
import * as core from '@janhq/core'
Base Classes
BaseExtension
All extensions extend from BaseExtension:
abstract class BaseExtension implements ExtensionType {
name: string
productName?: string
url: string
active?: boolean
description?: string
version?: string
abstract onLoad(): void
abstract onUnload(): void
type(): ExtensionTypeEnum | undefined
compatibility(): Compatibility | undefined
// Settings management
async registerSettings(settings: SettingComponentProps[]): Promise<void>
async getSettings(): Promise<SettingComponentProps[]>
async getSetting<T>(key: string, defaultValue: T): Promise<T>
async updateSettings(componentProps: Partial<SettingComponentProps>[]): Promise<void>
onSettingUpdate<T>(key: string, value: T): void
// Model management
async registerModels(models: Model[]): Promise<void>
// Installation
async install(): Promise<void>
}
Key Methods
Called when the extension is loaded. Initialize your extension here.
Called when the extension is unloaded. Clean up resources here.
type
() => ExtensionTypeEnum | undefined
Returns the extension type. Return undefined for generic extensions.
registerSettings
(settings: SettingComponentProps[]) => Promise<void>
Register settings for your extension that appear in Jan’s UI.
getSetting
<T>(key: string, defaultValue: T) => Promise<T>
Get a setting value by key. Returns defaultValue if not found.
updateSettings
(settings: Partial<SettingComponentProps>[]) => Promise<void>
Update one or more settings programmatically.
onSettingUpdate
<T>(key: string, value: T) => void
Called when a setting is updated by the user. Override to react to changes.
registerModels
(models: Model[]) => Promise<void>
Register models that can be used in Jan.
InferenceExtension
Extends BaseExtension for inference implementations:
abstract class InferenceExtension extends BaseExtension implements InferenceInterface {
type(): ExtensionTypeEnum {
return ExtensionTypeEnum.Inference
}
abstract inference(data: MessageRequest): Promise<ThreadMessage>
}
inference
(data: MessageRequest) => Promise<ThreadMessage>
required
Perform inference on a message request and return the response.
ConversationalExtension
Extends BaseExtension for conversation management:
abstract class ConversationalExtension extends BaseExtension {
type(): ExtensionTypeEnum {
return ExtensionTypeEnum.Conversational
}
abstract listThreads(): Promise<Thread[]>
abstract createThread(thread: Partial<Thread>): Promise<Thread>
abstract modifyThread(thread: Thread): Promise<void>
abstract deleteThread(threadId: string): Promise<void>
abstract createMessage(message: Partial<ThreadMessage>): Promise<ThreadMessage>
abstract modifyMessage(message: ThreadMessage): Promise<ThreadMessage>
abstract deleteMessage(threadId: string, messageId: string): Promise<void>
abstract listMessages(threadId: string): Promise<ThreadMessage[]>
abstract getThreadAssistant(threadId: string): Promise<ThreadAssistantInfo>
abstract createThreadAssistant(threadId: string, assistant: ThreadAssistantInfo): Promise<ThreadAssistantInfo>
abstract modifyThreadAssistant(threadId: string, assistant: ThreadAssistantInfo): Promise<ThreadAssistantInfo>
}
AssistantExtension
Extends BaseExtension for assistant management:
abstract class AssistantExtension extends BaseExtension implements AssistantInterface {
type(): ExtensionTypeEnum {
return ExtensionTypeEnum.Assistant
}
abstract createAssistant(assistant: Assistant): Promise<void>
abstract deleteAssistant(assistant: Assistant): Promise<void>
abstract getAssistants(): Promise<Assistant[]>
}
Extension Types
enum ExtensionTypeEnum {
Assistant = 'assistant',
Conversational = 'conversational',
Inference = 'inference',
Model = 'model',
SystemMonitoring = 'systemMonitoring',
MCP = 'mcp',
HuggingFace = 'huggingFace',
Engine = 'engine',
Hardware = 'hardware',
RAG = 'rag',
VectorDB = 'vectorDB'
}
Event System
The event system allows extensions to communicate:
const events = {
on: (eventName: string, handler: Function) => void,
off: (eventName: string, handler: Function) => void,
emit: (eventName: string, object: any) => void
}
Message Events
enum MessageEvent {
OnMessageSent = 'OnMessageSent',
OnMessageResponse = 'OnMessageResponse',
OnMessageUpdate = 'OnMessageUpdate'
}
Example:
import { events, MessageEvent } from '@janhq/core'
events.on(MessageEvent.OnMessageSent, (data: MessageRequest) => {
console.log('User sent:', data)
})
events.emit(MessageEvent.OnMessageResponse, responseMessage)
Inference Events
enum InferenceEvent {
OnInferenceStopped = 'OnInferenceStopped'
}
Core Types
Model
type Model = {
object: string // "model"
version: string // Model version
format: string // "gguf", "safetensors", etc.
sources: ModelArtifact[] // Download sources
id: string // Unique identifier
model?: string // Model identifier (modern)
name: string // Human-readable name
created: number // Unix timestamp
description: string // Model description
settings: ModelSettingParams
parameters: ModelRuntimeParams
metadata: ModelMetadata
engine: string // "llamacpp", "openai", etc.
}
type ModelArtifact = {
filename: string
url: string
}
type ModelMetadata = {
author: string
tags: string[]
size: number // Size in bytes
cover?: string // Cover image URL
default_ctx_len?: number
default_max_tokens?: number
}
type ModelSettingParams = {
ctx_len?: number
ngl?: number
embedding?: boolean
n_parallel?: number
cpu_threads?: number
prompt_template?: string
system_prompt?: string
temperature?: number
top_p?: number
top_k?: number
// ... more parameters
}
type ModelRuntimeParams = {
temperature?: number
max_temperature?: number
token_limit?: number
top_k?: number
top_p?: number
stream?: boolean
max_tokens?: number
stop?: string[]
frequency_penalty?: number
presence_penalty?: number
engine?: string
}
Thread
type Thread = {
id: string // Unique identifier (ULID)
object: string // "thread"
title: string // Thread title
assistants: ThreadAssistantInfo[]
created: number // Unix timestamp
updated: number // Unix timestamp
metadata?: Record<string, unknown>
}
type ThreadAssistantInfo = {
id: string
name: string
model: ModelInfo
instructions?: string
tools?: AssistantTool[]
}
ThreadMessage
type ThreadMessage = {
id: string // Unique identifier (ULID)
object: string // "thread.message"
thread_id: string
assistant_id?: string
attachments?: Array<Attachment> | null
role: ChatCompletionRole // 'user', 'assistant', 'system', 'tool'
content: ThreadContent[]
status: MessageStatus // 'ready', 'pending', 'error', 'stopped'
created_at: number // Unix timestamp
completed_at: number // Unix timestamp
metadata?: Record<string, unknown>
type?: string
error_code?: ErrorCode
tool_call_id?: string
}
enum MessageStatus {
Ready = 'ready',
Pending = 'pending',
Error = 'error',
Stopped = 'stopped'
}
enum ChatCompletionRole {
System = 'system',
Assistant = 'assistant',
User = 'user',
Tool = 'tool'
}
ThreadContent
type ThreadContent = {
type: ContentType
// For text and reasoning content
text?: ContentValue
// For image content
image_url?: ImageContentValue
// For tool call content
tool_call_id?: string
tool_name?: string
input?: unknown
output?: unknown
}
enum ContentType {
Text = 'text',
Reasoning = 'reasoning',
Image = 'image_url',
ToolCall = 'tool_call'
}
type ContentValue = {
value: string
annotations: string[]
}
type ImageContentValue = {
detail?: string
url?: string
}
MessageRequest
type MessageRequest = {
id?: string
threadId: string // Thread ID (deprecated, use thread)
assistantId?: string
attachments: Array<Attachment> | null
messages?: ChatCompletionMessage[]
model?: ModelInfo
thread?: Thread
tools?: MessageTool[]
engine?: string
type?: string
}
type ChatCompletionMessage = {
content?: string | ChatCompletionMessageContent[]
role: ChatCompletionRole
type?: string
output?: string
tool_call_id?: string
}
Assistant
type Assistant = {
avatar: string
thread_location: string | undefined
id: string
object: string // "assistant"
created_at: number // Unix timestamp
name: string
description?: string
model: string
instructions?: string
tools?: AssistantTool[]
file_ids: string[]
metadata?: Record<string, unknown>
}
type AssistantTool = {
type: string // 'retrieval', 'code_interpreter', etc.
enabled: boolean
useTimeWeightedRetriever?: boolean
settings: any
}
Settings
type SettingComponentProps = {
key: string
title: string
description: string
controllerType: ControllerType
controllerProps: SliderComponentProps | CheckboxComponentProps | InputComponentProps | DropdownComponentProps
extensionName?: string
requireModelReload?: boolean
configType?: 'runtime' | 'setting'
}
type ControllerType = 'slider' | 'checkbox' | 'input' | 'tag' | 'dropdown'
type InputComponentProps = {
placeholder: string
value: string | string[]
type?: 'password' | 'text' | 'email' | 'number' | 'tel' | 'url'
textAlign?: 'left' | 'right'
inputActions?: ('unobscure' | 'copy')[]
}
type SliderComponentProps = {
min: number
max: number
step: number
value: number
}
type CheckboxComponentProps = {
value: boolean
}
type DropdownComponentProps = {
value: string
type?: string
options?: DropdownOption[]
recommended?: string
}
type DropdownOption = {
name: string
value: string
}
Utility Functions
File System
const fs = {
existsSync: (path: string) => Promise<boolean>
mkdir: (path: string) => Promise<void>
readdirSync: (path: string) => Promise<string[]>
readFileSync: (path: string) => Promise<string>
writeFileSync: (path: string, data: string) => Promise<void>
rm: (path: string) => Promise<void>
mv: (from: string, to: string) => Promise<void>
fileStat: (path: string) => Promise<FileStat>
}
Path Utilities
const getJanDataFolderPath: () => Promise<string>
const joinPath: (paths: string[]) => Promise<string>
const baseName: (path: string) => Promise<string>
const dirName: (path: string) => Promise<string>
const getUserHomePath: () => Promise<string>
const getResourcePath: () => Promise<string>
UI Utilities
const openFileExplorer: (path: string) => Promise<void>
const openExternalUrl: (url: string) => Promise<void>
const showToast: (title: string, message: string) => void
const log: (message: string, fileName?: string) => void
Example: Complete Extension
import {
BaseExtension,
ExtensionTypeEnum,
events,
MessageEvent,
MessageRequest,
SettingComponentProps,
fs,
joinPath,
getJanDataFolderPath
} from '@janhq/core'
export default class MyCompleteExtension extends BaseExtension {
private dataPath: string = ''
async onLoad(): Promise<void> {
// Initialize data path
const janDataPath = await getJanDataFolderPath()
this.dataPath = await joinPath([janDataPath, 'my-extension'])
// Create data directory if it doesn't exist
if (!await fs.existsSync(this.dataPath)) {
await fs.mkdir(this.dataPath)
}
// Register settings
await this.registerSettings([
{
key: 'enabled',
title: 'Enable Extension',
description: 'Turn the extension on or off',
controllerType: 'checkbox',
controllerProps: { value: true }
},
{
key: 'api_key',
title: 'API Key',
description: 'Your API key',
controllerType: 'input',
controllerProps: {
placeholder: 'Enter API key',
value: '',
type: 'password',
inputActions: ['unobscure', 'copy']
}
},
{
key: 'temperature',
title: 'Temperature',
description: 'Model temperature',
controllerType: 'slider',
controllerProps: {
min: 0,
max: 2,
step: 0.1,
value: 0.7
}
}
])
// Listen to events
const enabled = await this.getSetting('enabled', true)
if (enabled) {
events.on(MessageEvent.OnMessageSent, this.handleMessage)
}
}
onUnload(): void {
events.off(MessageEvent.OnMessageSent, this.handleMessage)
}
onSettingUpdate<T>(key: string, value: T): void {
if (key === 'enabled') {
if (value) {
events.on(MessageEvent.OnMessageSent, this.handleMessage)
} else {
events.off(MessageEvent.OnMessageSent, this.handleMessage)
}
}
}
private handleMessage = async (data: MessageRequest) => {
const apiKey = await this.getSetting('api_key', '')
const temperature = await this.getSetting('temperature', 0.7)
console.log('Processing message with temp:', temperature)
// Your message processing logic here
}
}
Next Steps
Building Extensions
Learn how to package and distribute
Examples
Explore real extension examples