Skip to content

Commit b77c893

Browse files
committed
feat: support inserting current date into assistant prompt
1 parent 83b5321 commit b77c893

File tree

15 files changed

+257
-16
lines changed

15 files changed

+257
-16
lines changed

extensions/assistant-extension/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export default class JanAssistantExtension extends AssistantExtension {
7575
'Jan is a helpful desktop assistant that can reason through complex tasks and use tools to complete them on the user’s behalf.',
7676
model: '*',
7777
instructions:
78-
'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 you’re 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 don’t know or that needs verification\n- Never use tools just because they’re 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.',
78+
'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 you’re 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 don’t know or that needs verification\n- Never use tools just because they’re 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}}',
7979
tools: [
8080
{
8181
type: 'retrieval',

web-app/src/containers/dialogs/AddEditAssistant.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ export default function AddEditAssistant({
321321
className="resize-none"
322322
rows={4}
323323
/>
324+
<div className="text-xs text-main-view-fg/60">
325+
{t('assistants:instructionsDateHint')}
326+
</div>
324327
</div>
325328

326329
<div className="space-y-2 my-4">
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { renderHook, act } from '@testing-library/react'
2+
import { describe, it, expect, vi, beforeEach } from 'vitest'
3+
import { useChat } from '../useChat'
4+
5+
// Use hoisted storage for our mock to avoid hoist errors
6+
const hoisted = vi.hoisted(() => ({
7+
builderMock: vi.fn(() => ({
8+
addUserMessage: vi.fn(),
9+
addAssistantMessage: vi.fn(),
10+
getMessages: vi.fn(() => []),
11+
})),
12+
}))
13+
14+
vi.mock('@/lib/messages', () => ({
15+
CompletionMessagesBuilder: hoisted.builderMock,
16+
}))
17+
18+
// Mock dependencies similar to existing tests, but customize assistant
19+
vi.mock('../../hooks/usePrompt', () => ({
20+
usePrompt: vi.fn(() => ({ prompt: 'test prompt', setPrompt: vi.fn() })),
21+
}))
22+
23+
vi.mock('../../hooks/useAppState', () => ({
24+
useAppState: Object.assign(
25+
vi.fn(() => ({
26+
tools: [],
27+
updateTokenSpeed: vi.fn(),
28+
resetTokenSpeed: vi.fn(),
29+
updateTools: vi.fn(),
30+
updateStreamingContent: vi.fn(),
31+
updateLoadingModel: vi.fn(),
32+
setAbortController: vi.fn(),
33+
})),
34+
{ getState: vi.fn(() => ({ tokenSpeed: { tokensPerSecond: 10 } })) }
35+
),
36+
}))
37+
38+
vi.mock('../../hooks/useAssistant', () => ({
39+
useAssistant: vi.fn(() => ({
40+
assistants: [
41+
{
42+
id: 'test-assistant',
43+
instructions: 'Today is {{current_date}}',
44+
parameters: { stream: true },
45+
},
46+
],
47+
currentAssistant: {
48+
id: 'test-assistant',
49+
instructions: 'Today is {{current_date}}',
50+
parameters: { stream: true },
51+
},
52+
})),
53+
}))
54+
55+
vi.mock('../../hooks/useModelProvider', () => ({
56+
useModelProvider: vi.fn(() => ({
57+
getProviderByName: vi.fn(() => ({ provider: 'openai', models: [] })),
58+
selectedModel: { id: 'test-model', capabilities: ['tools'] },
59+
selectedProvider: 'openai',
60+
updateProvider: vi.fn(),
61+
})),
62+
}))
63+
64+
vi.mock('../../hooks/useThreads', () => ({
65+
useThreads: vi.fn(() => ({
66+
getCurrentThread: vi.fn(() => ({ id: 'test-thread', model: { id: 'test-model', provider: 'openai' } })),
67+
createThread: vi.fn(() => Promise.resolve({ id: 'test-thread', model: { id: 'test-model', provider: 'openai' } })),
68+
updateThreadTimestamp: vi.fn(),
69+
})),
70+
}))
71+
72+
vi.mock('../../hooks/useMessages', () => ({
73+
useMessages: vi.fn(() => ({ getMessages: vi.fn(() => []), addMessage: vi.fn() })),
74+
}))
75+
76+
vi.mock('../../hooks/useToolApproval', () => ({
77+
useToolApproval: vi.fn(() => ({ approvedTools: [], showApprovalModal: vi.fn(), allowAllMCPPermissions: false })),
78+
}))
79+
80+
vi.mock('../../hooks/useModelContextApproval', () => ({
81+
useContextSizeApproval: vi.fn(() => ({ showApprovalModal: vi.fn() })),
82+
}))
83+
84+
vi.mock('../../hooks/useModelLoad', () => ({
85+
useModelLoad: vi.fn(() => ({ setModelLoadError: vi.fn() })),
86+
}))
87+
88+
vi.mock('@tanstack/react-router', () => ({
89+
useRouter: vi.fn(() => ({ navigate: vi.fn() })),
90+
}))
91+
92+
vi.mock('@/lib/completion', () => ({
93+
emptyThreadContent: { thread_id: 'test-thread', content: '' },
94+
extractToolCall: vi.fn(),
95+
newUserThreadContent: vi.fn(() => ({ thread_id: 'test-thread', content: 'user message' })),
96+
newAssistantThreadContent: vi.fn(() => ({ thread_id: 'test-thread', content: 'assistant message' })),
97+
sendCompletion: vi.fn(() => Promise.resolve({ choices: [{ message: { content: '' } }] })),
98+
postMessageProcessing: vi.fn(),
99+
isCompletionResponse: vi.fn(() => true),
100+
}))
101+
102+
vi.mock('@/services/mcp', () => ({ getTools: vi.fn(() => Promise.resolve([])) }))
103+
104+
vi.mock('@/services/models', () => ({
105+
startModel: vi.fn(() => Promise.resolve()),
106+
stopModel: vi.fn(() => Promise.resolve()),
107+
stopAllModels: vi.fn(() => Promise.resolve()),
108+
}))
109+
110+
vi.mock('@/services/providers', () => ({ updateSettings: vi.fn(() => Promise.resolve()) }))
111+
112+
vi.mock('@tauri-apps/api/event', () => ({ listen: vi.fn(() => Promise.resolve(vi.fn())) }))
113+
114+
describe('useChat instruction rendering', () => {
115+
beforeEach(() => {
116+
vi.clearAllMocks()
117+
})
118+
119+
it('renders assistant instructions by replacing {{current_date}} with today', async () => {
120+
vi.useFakeTimers()
121+
vi.setSystemTime(new Date('2025-08-16T00:00:00Z'))
122+
123+
const { result } = renderHook(() => useChat())
124+
125+
await act(async () => {
126+
await result.current.sendMessage('Hello')
127+
})
128+
129+
expect(hoisted.builderMock).toHaveBeenCalled()
130+
const calls = (hoisted.builderMock as any).mock.calls as any[]
131+
const call = calls[0]
132+
expect(call[0]).toEqual([])
133+
expect(call[1]).toMatch(/^Today is /)
134+
expect(call[1]).not.toContain('{{current_date}}')
135+
136+
vi.useRealTimers()
137+
})
138+
})

web-app/src/hooks/useAssistant.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const defaultAssistant: Assistant = {
4343
description:
4444
'Jan is a helpful desktop assistant that can reason through complex tasks and use tools to complete them on the user’s behalf.',
4545
instructions:
46-
'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 you’re 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 don’t know or that needs verification\n- Never use tools just because they’re 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.',
46+
'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 you’re 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 don’t know or that needs verification\n- Never use tools just because they’re 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}}',
4747
}
4848

4949
export const useAssistant = create<AssistantState>()((set, get) => ({

web-app/src/hooks/useChat.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
sendCompletion,
1818
} from '@/lib/completion'
1919
import { CompletionMessagesBuilder } from '@/lib/messages'
20+
import { renderInstructions } from '@/lib/instructionTemplate'
2021
import { ChatCompletionMessageToolCall } from 'openai/resources'
2122
import { useAssistant } from './useAssistant'
2223
import { getTools } from '@/services/mcp'
@@ -245,7 +246,7 @@ export const useChat = () => {
245246

246247
const builder = new CompletionMessagesBuilder(
247248
messages,
248-
currentAssistant?.instructions
249+
renderInstructions(currentAssistant?.instructions)
249250
)
250251
if (troubleshooting) builder.addUserMessage(message)
251252

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { describe, it, expect, vi } from 'vitest'
2+
import { renderInstructions } from '../instructionTemplate'
3+
4+
describe('renderInstructions', () => {
5+
it('replaces {{current_date}} with today when no params provided', () => {
6+
vi.useFakeTimers()
7+
vi.setSystemTime(new Date('2025-08-16T00:00:00Z'))
8+
9+
const input = 'Today is {{current_date}}.'
10+
const out = renderInstructions(input)
11+
12+
expect(out).not.toBe(input)
13+
expect(out).toMatch(/^Today is /)
14+
expect(out).not.toContain('{{current_date}}')
15+
16+
vi.useRealTimers()
17+
})
18+
19+
it('replaces multiple occurrences of {{current_date}}', () => {
20+
const input = 'A {{current_date}} B {{current_date}} C'
21+
const out = renderInstructions(input)
22+
expect(out).not.toContain('{{current_date}}')
23+
expect(out.startsWith('A ')).toBe(true)
24+
expect(out.includes(' B ')).toBe(true)
25+
expect(out.endsWith(' C')).toBe(true)
26+
})
27+
})
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { formatDate } from '@/utils/formatDate'
2+
3+
/**
4+
* Render assistant instructions by replacing supported placeholders.
5+
* Supported placeholders:
6+
* - {{current_date}}: Inserts today’s date (UTC, long month), e.g., August 16, 2025.
7+
*/
8+
export function renderInstructions(instructions: string): string
9+
export function renderInstructions(
10+
instructions?: string
11+
): string | undefined
12+
export function renderInstructions(
13+
instructions?: string
14+
): string | undefined {
15+
if (!instructions) return instructions
16+
17+
const currentDateStr = formatDate(new Date(), { includeTime: false })
18+
19+
// Replace current_date (allow spaces inside braces).
20+
let rendered = instructions
21+
rendered = rendered.replace(/\{\{\s*current_date\s*\}\}/gi, currentDateStr)
22+
return rendered
23+
}

web-app/src/locales/de-DE/assistants.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,6 @@
2929
"save": "Speichern",
3030
"createNew": "Neuen Assistenten anlegen",
3131
"personality": "Persönlichkeit",
32-
"capabilities": "Fähigkeiten"
32+
"capabilities": "Fähigkeiten",
33+
"instructionsDateHint": "Tipp: Verwenden Sie {{current_date}}, um das heutige Datum einzufügen."
3334
}

web-app/src/locales/en/assistants.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,6 @@
2929
"save": "Save",
3030
"createNew": "Create New Assistant",
3131
"personality": "Personality",
32-
"capabilities": "Capabilities"
32+
"capabilities": "Capabilities",
33+
"instructionsDateHint": "Tip: Use {{current_date}} to insert today’s date."
3334
}

web-app/src/locales/id/assistants.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,6 @@
2929
"save": "Simpan",
3030
"createNew": "Buat Asisten Baru",
3131
"personality": "Kepribadian",
32-
"capabilities": "Kemampuan"
32+
"capabilities": "Kemampuan",
33+
"instructionsDateHint": "Tips: Gunakan {{current_date}} untuk menyisipkan tanggal hari ini."
3334
}

0 commit comments

Comments
 (0)