2025-08-17 00:24:00 -07:00

102 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Assistant, AssistantExtension, fs, joinPath } from '@janhq/core'
export default class JanAssistantExtension extends AssistantExtension {
async onLoad() {
if (!(await fs.existsSync('file://assistants'))) {
await fs.mkdir('file://assistants')
}
const assistants = await this.getAssistants()
if (assistants.length === 0) {
await this.createAssistant(this.defaultAssistant)
}
}
/**
* Called when the extension is unloaded.
*/
onUnload(): void {}
async getAssistants(): Promise<Assistant[]> {
if (!(await fs.existsSync('file://assistants')))
return [this.defaultAssistant]
const assistants = await fs.readdirSync('file://assistants')
const assistantsData: Assistant[] = []
for (const assistant of assistants) {
const assistantPath = await joinPath([
'file://assistants',
assistant,
'assistant.json',
])
if (!(await fs.existsSync(assistantPath))) {
console.warn(`Assistant file not found: ${assistantPath}`)
continue
}
try {
const assistantData = JSON.parse(await fs.readFileSync(assistantPath))
assistantsData.push(assistantData as Assistant)
} catch (error) {
console.error(`Failed to read assistant ${assistant}:`, error)
}
}
return assistantsData
}
async createAssistant(assistant: Assistant): Promise<void> {
const assistantPath = await joinPath([
'file://assistants',
assistant.id,
'assistant.json',
])
const assistantFolder = await joinPath(['file://assistants', assistant.id])
if (!(await fs.existsSync(assistantFolder))) {
await fs.mkdir(assistantFolder)
}
await fs.writeFileSync(assistantPath, JSON.stringify(assistant, null, 2))
}
async deleteAssistant(assistant: Assistant): Promise<void> {
const assistantPath = await joinPath([
'file://assistants',
assistant.id,
'assistant.json',
])
if (await fs.existsSync(assistantPath)) {
await fs.rm(assistantPath)
}
}
private defaultAssistant: Assistant = {
avatar: '👋',
thread_location: undefined,
id: 'jan',
object: 'assistant',
created_at: Date.now() / 1000,
name: 'Jan',
description:
'Jan is a helpful desktop assistant that can reason through complex tasks and use tools to complete them on the users behalf.',
model: '*',
instructions:
'You are a helpful AI assistant. Your primary goal is to assist users with their questions and tasks to the best of your abilities.\n\nWhen responding:\n- Answer directly from your knowledge when you can\n- Be concise, clear, and helpful\n- Admit when youre unsure rather than making things up\n\nIf tools are available to you:\n- Only use tools when they add real value to your response\n- Use tools when the user explicitly asks (e.g., "search for...", "calculate...", "run this code")\n- Use tools for information you dont know or that needs verification\n- Never use tools just because theyre available\n\nWhen using tools:\n- Use one tool at a time and wait for results\n- Use actual values as arguments, not variable names\n- Learn from each result before deciding next steps\n- Avoid repeating the same tool call with identical parameters\n\nRemember: Most questions can be answered without tools. Think first whether you need them.\n\nCurrent date: {{current_date}}',
tools: [
{
type: 'retrieval',
enabled: false,
useTimeWeightedRetriever: false,
settings: {
top_k: 2,
chunk_size: 1024,
chunk_overlap: 64,
retrieval_template: `Use the following pieces of context to answer the question at the end.
----------------
CONTEXT: {CONTEXT}
----------------
QUESTION: {QUESTION}
----------------
Helpful Answer:`,
},
},
],
file_ids: [],
metadata: undefined,
}
}