Skip to content

Commit da4336c

Browse files
authored
Merge pull request #4338 from janhq/main
Merge bug fixes from 0.5.12 into dev
2 parents 7140978 + 3cd4db0 commit da4336c

File tree

26 files changed

+355
-192
lines changed

26 files changed

+355
-192
lines changed

extensions/conversational-extension/src/index.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
4040
async listThreads(): Promise<Thread[]> {
4141
return this.queue.add(() =>
4242
ky
43-
.get(`${API_URL}/v1/threads`)
43+
.get(`${API_URL}/v1/threads?limit=-1`)
4444
.json<ThreadList>()
4545
.then((e) => e.data)
4646
) as Promise<Thread[]>
@@ -133,7 +133,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
133133
async listMessages(threadId: string): Promise<ThreadMessage[]> {
134134
return this.queue.add(() =>
135135
ky
136-
.get(`${API_URL}/v1/threads/${threadId}/messages?order=asc`)
136+
.get(`${API_URL}/v1/threads/${threadId}/messages?order=asc&limit=-1`)
137137
.json<MessageList>()
138138
.then((e) => e.data)
139139
) as Promise<ThreadMessage[]>
@@ -147,7 +147,9 @@ export default class CortexConversationalExtension extends ConversationalExtensi
147147
*/
148148
async getThreadAssistant(threadId: string): Promise<ThreadAssistantInfo> {
149149
return this.queue.add(() =>
150-
ky.get(`${API_URL}/v1/assistants/${threadId}`).json<ThreadAssistantInfo>()
150+
ky
151+
.get(`${API_URL}/v1/assistants/${threadId}?limit=-1`)
152+
.json<ThreadAssistantInfo>()
151153
) as Promise<ThreadAssistantInfo>
152154
}
153155
/**
@@ -188,7 +190,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
188190
* Do health check on cortex.cpp
189191
* @returns
190192
*/
191-
healthz(): Promise<void> {
193+
async healthz(): Promise<void> {
192194
return ky
193195
.get(`${API_URL}/healthz`, {
194196
retry: { limit: 20, delay: () => 500, methods: ['get'] },
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.0.5-rc2
1+
1.0.6

extensions/inference-openai-extension/resources/models.json

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,63 @@
8585
},
8686
"engine": "openai"
8787
},
88+
{
89+
"sources": [
90+
{
91+
"url": "https://openai.com"
92+
}
93+
],
94+
"id": "gpt-4o-mini",
95+
"object": "model",
96+
"name": "OpenAI GPT 4o-mini",
97+
"version": "1.1",
98+
"description": "GPT-4o mini (“o” for “omni”) is a fast, affordable small model for focused tasks.",
99+
"format": "api",
100+
"settings": {
101+
"vision_model": true
102+
},
103+
"parameters": {
104+
"max_tokens": 16384,
105+
"temperature": 0.7,
106+
"top_p": 0.95,
107+
"stream": true,
108+
"stop": [],
109+
"frequency_penalty": 0,
110+
"presence_penalty": 0
111+
},
112+
"metadata": {
113+
"author": "OpenAI",
114+
"tags": ["General"]
115+
},
116+
"engine": "openai"
117+
},
118+
{
119+
"sources": [
120+
{
121+
"url": "https://openai.com"
122+
}
123+
],
124+
"id": "o1",
125+
"object": "model",
126+
"name": "OpenAI o1",
127+
"version": "1.0",
128+
"description": "OpenAI o1 is a new model with complex reasoning",
129+
"format": "api",
130+
"settings": {},
131+
"parameters": {
132+
"max_tokens": 100000,
133+
"temperature": 1,
134+
"top_p": 1,
135+
"stream": true,
136+
"frequency_penalty": 0,
137+
"presence_penalty": 0
138+
},
139+
"metadata": {
140+
"author": "OpenAI",
141+
"tags": ["General"]
142+
},
143+
"engine": "openai"
144+
},
88145
{
89146
"sources": [
90147
{

extensions/model-extension/src/cortex.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class CortexAPI implements ICortexAPI {
5353
*/
5454
getModels(): Promise<Model[]> {
5555
return this.queue
56-
.add(() => ky.get(`${API_URL}/v1/models`).json<ModelList>())
56+
.add(() => ky.get(`${API_URL}/v1/models?limit=-1`).json<ModelList>())
5757
.then((e) =>
5858
typeof e === 'object' ? e.data.map((e) => this.transformModel(e)) : []
5959
)

joi/src/core/TextArea/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
3434
return (
3535
<div className="textarea__wrapper">
3636
<textarea
37-
className={twMerge('textarea', className)}
37+
className={twMerge(
38+
'textarea',
39+
className,
40+
autoResize && 'resize-none'
41+
)}
3842
ref={autoResize ? textareaRef : ref}
3943
{...props}
4044
/>

web/containers/ModelDropdown/index.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,7 @@ const ModelDropdown = ({
183183
if (!activeThread) return
184184
const modelId = activeAssistant?.model?.id
185185

186-
let model = downloadedModels.find((model) => model.id === modelId)
187-
if (!model) {
188-
model = undefined
189-
}
186+
const model = downloadedModels.find((model) => model.id === modelId)
190187
setSelectedModel(model)
191188
}, [
192189
recommendedModel,
@@ -378,14 +375,14 @@ const ModelDropdown = ({
378375
!selectedModel && 'text-[hsla(var(--text-tertiary))]'
379376
)}
380377
>
381-
{selectedModel?.name || 'Select Model'}
378+
{selectedModel?.name || 'Select a model'}
382379
</span>
383380
</Badge>
384381
) : (
385382
<Input
386383
value={selectedModel?.name || ''}
387384
className="cursor-pointer"
388-
placeholder="Select Model"
385+
placeholder="Select a model"
389386
disabled={disabled}
390387
readOnly
391388
suffixIcon={

web/containers/Providers/ModelHandler.tsx

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
extractInferenceParams,
1919
ModelExtension,
2020
} from '@janhq/core'
21-
import { useAtomValue, useSetAtom } from 'jotai'
21+
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
2222
import { ulid } from 'ulidx'
2323

2424
import { activeModelAtom, stateModelAtom } from '@/hooks/useActiveModel'
@@ -32,6 +32,7 @@ import {
3232
updateMessageAtom,
3333
tokenSpeedAtom,
3434
deleteMessageAtom,
35+
subscribedGeneratingMessageAtom,
3536
} from '@/helpers/atoms/ChatMessage.atom'
3637
import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom'
3738
import {
@@ -40,6 +41,7 @@ import {
4041
isGeneratingResponseAtom,
4142
updateThreadAtom,
4243
getActiveThreadModelParamsAtom,
44+
activeThreadAtom,
4345
} from '@/helpers/atoms/Thread.atom'
4446

4547
const maxWordForThreadTitle = 10
@@ -54,6 +56,10 @@ export default function ModelHandler() {
5456
const activeModel = useAtomValue(activeModelAtom)
5557
const setActiveModel = useSetAtom(activeModelAtom)
5658
const setStateModel = useSetAtom(stateModelAtom)
59+
const [subscribedGeneratingMessage, setSubscribedGeneratingMessage] = useAtom(
60+
subscribedGeneratingMessageAtom
61+
)
62+
const activeThread = useAtomValue(activeThreadAtom)
5763

5864
const updateThreadWaiting = useSetAtom(updateThreadWaitingForResponseAtom)
5965
const threads = useAtomValue(threadsAtom)
@@ -62,11 +68,17 @@ export default function ModelHandler() {
6268
const setIsGeneratingResponse = useSetAtom(isGeneratingResponseAtom)
6369
const updateThread = useSetAtom(updateThreadAtom)
6470
const messagesRef = useRef(messages)
71+
const messageGenerationSubscriber = useRef(subscribedGeneratingMessage)
6572
const activeModelRef = useRef(activeModel)
73+
const activeThreadRef = useRef(activeThread)
6674
const activeModelParams = useAtomValue(getActiveThreadModelParamsAtom)
6775
const activeModelParamsRef = useRef(activeModelParams)
6876
const setTokenSpeed = useSetAtom(tokenSpeedAtom)
6977

78+
useEffect(() => {
79+
activeThreadRef.current = activeThread
80+
}, [activeThread])
81+
7082
useEffect(() => {
7183
threadsRef.current = threads
7284
}, [threads])
@@ -87,6 +99,10 @@ export default function ModelHandler() {
8799
activeModelParamsRef.current = activeModelParams
88100
}, [activeModelParams])
89101

102+
useEffect(() => {
103+
messageGenerationSubscriber.current = subscribedGeneratingMessage
104+
}, [subscribedGeneratingMessage])
105+
90106
const onNewMessageResponse = useCallback(
91107
async (message: ThreadMessage) => {
92108
if (message.type === MessageRequestType.Thread) {
@@ -179,12 +195,19 @@ export default function ModelHandler() {
179195

180196
const updateThreadMessage = useCallback(
181197
(message: ThreadMessage) => {
182-
updateMessage(
183-
message.id,
184-
message.thread_id,
185-
message.content,
186-
message.status
187-
)
198+
if (
199+
messageGenerationSubscriber.current &&
200+
message.thread_id === activeThreadRef.current?.id &&
201+
!messageGenerationSubscriber.current!.thread_id
202+
) {
203+
updateMessage(
204+
message.id,
205+
message.thread_id,
206+
message.content,
207+
message.status
208+
)
209+
}
210+
188211
if (message.status === MessageStatus.Pending) {
189212
if (message.content.length) {
190213
setIsGeneratingResponse(false)
@@ -244,6 +267,7 @@ export default function ModelHandler() {
244267
const metadata = {
245268
...thread.metadata,
246269
...(messageContent && { lastMessage: messageContent }),
270+
updated_at: Date.now(),
247271
}
248272

249273
updateThread({
@@ -302,15 +326,10 @@ export default function ModelHandler() {
302326

303327
const generateThreadTitle = (message: ThreadMessage, thread: Thread) => {
304328
// If this is the first ever prompt in the thread
305-
if (
306-
(thread.title ?? thread.metadata?.title)?.trim() !== defaultThreadTitle
307-
) {
329+
if ((thread.title ?? thread.metadata?.title)?.trim() !== defaultThreadTitle)
308330
return
309-
}
310331

311-
if (!activeModelRef.current) {
312-
return
313-
}
332+
if (!activeModelRef.current) return
314333

315334
// Check model engine; we don't want to generate a title when it's not a local engine. remote model using first promp
316335
if (!isLocalEngine(activeModelRef.current?.engine as InferenceEngine)) {
@@ -332,6 +351,7 @@ export default function ModelHandler() {
332351
...updatedThread,
333352
})
334353
})
354+
.catch(console.error)
335355
}
336356

337357
// This is the first time message comes in on a new thread

web/helpers/atoms/ChatMessage.atom.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ export const chatMessages = atom(
3535
}
3636
)
3737

38+
/**
39+
* Store subscribed generating message thread
40+
*/
41+
export const subscribedGeneratingMessageAtom = atom<{
42+
thread_id?: string
43+
}>({})
44+
3845
/**
3946
* Stores the status of the messages load for each thread
4047
*/
@@ -175,6 +182,17 @@ export const updateMessageAtom = atom(
175182
// Update thread last message
176183
if (text.length)
177184
set(updateThreadStateLastMessageAtom, conversationId, text)
185+
} else {
186+
set(addNewMessageAtom, {
187+
id,
188+
thread_id: conversationId,
189+
content: text,
190+
status,
191+
role: ChatCompletionRole.Assistant,
192+
created_at: Date.now() / 1000,
193+
completed_at: Date.now() / 1000,
194+
object: 'thread.message',
195+
})
178196
}
179197
}
180198
)

web/helpers/atoms/Setting.atom.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,7 @@ export const CHAT_WIDTH = 'chatWidth'
2020
export const themesOptionsAtom = atomWithStorage<
2121
{ name: string; value: string }[]
2222
>(THEME_OPTIONS, [], undefined, { getOnInit: true })
23-
export const janThemesPathAtom = atomWithStorage<string | undefined>(
24-
THEME_PATH,
25-
undefined,
26-
undefined,
27-
{ getOnInit: true }
28-
)
23+
2924
export const selectedThemeIdAtom = atomWithStorage<string>(
3025
THEME,
3126
'',

web/helpers/atoms/Thread.atom.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,26 @@ export const waitingToSendMessage = atom<boolean | undefined>(undefined)
125125
*/
126126
export const isGeneratingResponseAtom = atom<boolean | undefined>(undefined)
127127

128+
/**
129+
* Create a new thread and add it to the thread list
130+
*/
131+
export const createNewThreadAtom = atom(null, (get, set, newThread: Thread) => {
132+
// create thread state for this new thread
133+
const currentState = { ...get(threadStatesAtom) }
134+
135+
const threadState: ThreadState = {
136+
hasMore: false,
137+
waitingForResponse: false,
138+
lastMessage: undefined,
139+
}
140+
currentState[newThread.id] = threadState
141+
set(threadStatesAtom, currentState)
142+
143+
// add the new thread on top of the thread list to the state
144+
const threads = get(threadsAtom)
145+
set(threadsAtom, [newThread, ...threads])
146+
})
147+
128148
/**
129149
* Remove a thread state from the atom
130150
*/
@@ -180,12 +200,12 @@ export const updateThreadAtom = atom(
180200
)
181201

182202
// sort new threads based on updated at
183-
threads.sort((thread1, thread2) => {
184-
const aDate = new Date(thread1.updated ?? 0)
185-
const bDate = new Date(thread2.updated ?? 0)
186-
return bDate.getTime() - aDate.getTime()
203+
threads.sort((a, b) => {
204+
return ((a.metadata?.updated_at as number) ?? 0) >
205+
((b.metadata?.updated_at as number) ?? 0)
206+
? -1
207+
: 1
187208
})
188-
189209
set(threadsAtom, threads)
190210
}
191211
)

0 commit comments

Comments
 (0)