Skip to content

Commit

Permalink
Merge pull request #4338 from janhq/main
Browse files Browse the repository at this point in the history
Merge bug fixes from 0.5.12 into dev
  • Loading branch information
louis-menlo authored Dec 26, 2024
2 parents 7140978 + 3cd4db0 commit da4336c
Show file tree
Hide file tree
Showing 26 changed files with 355 additions and 192 deletions.
10 changes: 6 additions & 4 deletions extensions/conversational-extension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
async listThreads(): Promise<Thread[]> {
return this.queue.add(() =>
ky
.get(`${API_URL}/v1/threads`)
.get(`${API_URL}/v1/threads?limit=-1`)
.json<ThreadList>()
.then((e) => e.data)
) as Promise<Thread[]>
Expand Down Expand Up @@ -133,7 +133,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
async listMessages(threadId: string): Promise<ThreadMessage[]> {
return this.queue.add(() =>
ky
.get(`${API_URL}/v1/threads/${threadId}/messages?order=asc`)
.get(`${API_URL}/v1/threads/${threadId}/messages?order=asc&limit=-1`)
.json<MessageList>()
.then((e) => e.data)
) as Promise<ThreadMessage[]>
Expand All @@ -147,7 +147,9 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/
async getThreadAssistant(threadId: string): Promise<ThreadAssistantInfo> {
return this.queue.add(() =>
ky.get(`${API_URL}/v1/assistants/${threadId}`).json<ThreadAssistantInfo>()
ky
.get(`${API_URL}/v1/assistants/${threadId}?limit=-1`)
.json<ThreadAssistantInfo>()
) as Promise<ThreadAssistantInfo>
}
/**
Expand Down Expand Up @@ -188,7 +190,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
* Do health check on cortex.cpp
* @returns
*/
healthz(): Promise<void> {
async healthz(): Promise<void> {
return ky
.get(`${API_URL}/healthz`, {
retry: { limit: 20, delay: () => 500, methods: ['get'] },
Expand Down
2 changes: 1 addition & 1 deletion extensions/inference-cortex-extension/bin/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.5-rc2
1.0.6
57 changes: 57 additions & 0 deletions extensions/inference-openai-extension/resources/models.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,63 @@
},
"engine": "openai"
},
{
"sources": [
{
"url": "https://openai.com"
}
],
"id": "gpt-4o-mini",
"object": "model",
"name": "OpenAI GPT 4o-mini",
"version": "1.1",
"description": "GPT-4o mini (“o” for “omni”) is a fast, affordable small model for focused tasks.",
"format": "api",
"settings": {
"vision_model": true
},
"parameters": {
"max_tokens": 16384,
"temperature": 0.7,
"top_p": 0.95,
"stream": true,
"stop": [],
"frequency_penalty": 0,
"presence_penalty": 0
},
"metadata": {
"author": "OpenAI",
"tags": ["General"]
},
"engine": "openai"
},
{
"sources": [
{
"url": "https://openai.com"
}
],
"id": "o1",
"object": "model",
"name": "OpenAI o1",
"version": "1.0",
"description": "OpenAI o1 is a new model with complex reasoning",
"format": "api",
"settings": {},
"parameters": {
"max_tokens": 100000,
"temperature": 1,
"top_p": 1,
"stream": true,
"frequency_penalty": 0,
"presence_penalty": 0
},
"metadata": {
"author": "OpenAI",
"tags": ["General"]
},
"engine": "openai"
},
{
"sources": [
{
Expand Down
2 changes: 1 addition & 1 deletion extensions/model-extension/src/cortex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class CortexAPI implements ICortexAPI {
*/
getModels(): Promise<Model[]> {
return this.queue
.add(() => ky.get(`${API_URL}/v1/models`).json<ModelList>())
.add(() => ky.get(`${API_URL}/v1/models?limit=-1`).json<ModelList>())
.then((e) =>
typeof e === 'object' ? e.data.map((e) => this.transformModel(e)) : []
)
Expand Down
6 changes: 5 additions & 1 deletion joi/src/core/TextArea/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
return (
<div className="textarea__wrapper">
<textarea
className={twMerge('textarea', className)}
className={twMerge(
'textarea',
className,
autoResize && 'resize-none'
)}
ref={autoResize ? textareaRef : ref}
{...props}
/>
Expand Down
9 changes: 3 additions & 6 deletions web/containers/ModelDropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,7 @@ const ModelDropdown = ({
if (!activeThread) return
const modelId = activeAssistant?.model?.id

let model = downloadedModels.find((model) => model.id === modelId)
if (!model) {
model = undefined
}
const model = downloadedModels.find((model) => model.id === modelId)
setSelectedModel(model)
}, [
recommendedModel,
Expand Down Expand Up @@ -378,14 +375,14 @@ const ModelDropdown = ({
!selectedModel && 'text-[hsla(var(--text-tertiary))]'
)}
>
{selectedModel?.name || 'Select Model'}
{selectedModel?.name || 'Select a model'}
</span>
</Badge>
) : (
<Input
value={selectedModel?.name || ''}
className="cursor-pointer"
placeholder="Select Model"
placeholder="Select a model"
disabled={disabled}
readOnly
suffixIcon={
Expand Down
48 changes: 34 additions & 14 deletions web/containers/Providers/ModelHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
extractInferenceParams,
ModelExtension,
} from '@janhq/core'
import { useAtomValue, useSetAtom } from 'jotai'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import { ulid } from 'ulidx'

import { activeModelAtom, stateModelAtom } from '@/hooks/useActiveModel'
Expand All @@ -32,6 +32,7 @@ import {
updateMessageAtom,
tokenSpeedAtom,
deleteMessageAtom,
subscribedGeneratingMessageAtom,
} from '@/helpers/atoms/ChatMessage.atom'
import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom'
import {
Expand All @@ -40,6 +41,7 @@ import {
isGeneratingResponseAtom,
updateThreadAtom,
getActiveThreadModelParamsAtom,
activeThreadAtom,
} from '@/helpers/atoms/Thread.atom'

const maxWordForThreadTitle = 10
Expand All @@ -54,6 +56,10 @@ export default function ModelHandler() {
const activeModel = useAtomValue(activeModelAtom)
const setActiveModel = useSetAtom(activeModelAtom)
const setStateModel = useSetAtom(stateModelAtom)
const [subscribedGeneratingMessage, setSubscribedGeneratingMessage] = useAtom(

Check warning on line 59 in web/containers/Providers/ModelHandler.tsx

View workflow job for this annotation

GitHub Actions / test-on-macos

'setSubscribedGeneratingMessage' is assigned a value but never used

Check warning on line 59 in web/containers/Providers/ModelHandler.tsx

View workflow job for this annotation

GitHub Actions / test-on-ubuntu

'setSubscribedGeneratingMessage' is assigned a value but never used

Check warning on line 59 in web/containers/Providers/ModelHandler.tsx

View workflow job for this annotation

GitHub Actions / test-on-windows (mcafee)

'setSubscribedGeneratingMessage' is assigned a value but never used

Check warning on line 59 in web/containers/Providers/ModelHandler.tsx

View workflow job for this annotation

GitHub Actions / test-on-windows (default-windows-security)

'setSubscribedGeneratingMessage' is assigned a value but never used

Check warning on line 59 in web/containers/Providers/ModelHandler.tsx

View workflow job for this annotation

GitHub Actions / test-on-windows (bit-defender)

'setSubscribedGeneratingMessage' is assigned a value but never used

Check warning on line 59 in web/containers/Providers/ModelHandler.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

'setSubscribedGeneratingMessage' is assigned a value but never used
subscribedGeneratingMessageAtom
)
const activeThread = useAtomValue(activeThreadAtom)

const updateThreadWaiting = useSetAtom(updateThreadWaitingForResponseAtom)
const threads = useAtomValue(threadsAtom)
Expand All @@ -62,11 +68,17 @@ export default function ModelHandler() {
const setIsGeneratingResponse = useSetAtom(isGeneratingResponseAtom)
const updateThread = useSetAtom(updateThreadAtom)
const messagesRef = useRef(messages)
const messageGenerationSubscriber = useRef(subscribedGeneratingMessage)
const activeModelRef = useRef(activeModel)
const activeThreadRef = useRef(activeThread)
const activeModelParams = useAtomValue(getActiveThreadModelParamsAtom)
const activeModelParamsRef = useRef(activeModelParams)
const setTokenSpeed = useSetAtom(tokenSpeedAtom)

useEffect(() => {
activeThreadRef.current = activeThread
}, [activeThread])

useEffect(() => {
threadsRef.current = threads
}, [threads])
Expand All @@ -87,6 +99,10 @@ export default function ModelHandler() {
activeModelParamsRef.current = activeModelParams
}, [activeModelParams])

useEffect(() => {
messageGenerationSubscriber.current = subscribedGeneratingMessage
}, [subscribedGeneratingMessage])

const onNewMessageResponse = useCallback(
async (message: ThreadMessage) => {
if (message.type === MessageRequestType.Thread) {
Expand Down Expand Up @@ -179,12 +195,19 @@ export default function ModelHandler() {

const updateThreadMessage = useCallback(
(message: ThreadMessage) => {
updateMessage(
message.id,
message.thread_id,
message.content,
message.status
)
if (
messageGenerationSubscriber.current &&
message.thread_id === activeThreadRef.current?.id &&
!messageGenerationSubscriber.current!.thread_id
) {
updateMessage(
message.id,
message.thread_id,
message.content,
message.status
)
}

if (message.status === MessageStatus.Pending) {
if (message.content.length) {
setIsGeneratingResponse(false)
Expand Down Expand Up @@ -244,6 +267,7 @@ export default function ModelHandler() {
const metadata = {
...thread.metadata,
...(messageContent && { lastMessage: messageContent }),
updated_at: Date.now(),
}

updateThread({
Expand Down Expand Up @@ -302,15 +326,10 @@ export default function ModelHandler() {

const generateThreadTitle = (message: ThreadMessage, thread: Thread) => {
// If this is the first ever prompt in the thread
if (
(thread.title ?? thread.metadata?.title)?.trim() !== defaultThreadTitle
) {
if ((thread.title ?? thread.metadata?.title)?.trim() !== defaultThreadTitle)
return
}

if (!activeModelRef.current) {
return
}
if (!activeModelRef.current) return

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

// This is the first time message comes in on a new thread
Expand Down
18 changes: 18 additions & 0 deletions web/helpers/atoms/ChatMessage.atom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ export const chatMessages = atom(
}
)

/**
* Store subscribed generating message thread
*/
export const subscribedGeneratingMessageAtom = atom<{
thread_id?: string
}>({})

/**
* Stores the status of the messages load for each thread
*/
Expand Down Expand Up @@ -175,6 +182,17 @@ export const updateMessageAtom = atom(
// Update thread last message
if (text.length)
set(updateThreadStateLastMessageAtom, conversationId, text)
} else {
set(addNewMessageAtom, {
id,
thread_id: conversationId,
content: text,
status,
role: ChatCompletionRole.Assistant,
created_at: Date.now() / 1000,
completed_at: Date.now() / 1000,
object: 'thread.message',
})
}
}
)
7 changes: 1 addition & 6 deletions web/helpers/atoms/Setting.atom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,7 @@ export const CHAT_WIDTH = 'chatWidth'
export const themesOptionsAtom = atomWithStorage<
{ name: string; value: string }[]
>(THEME_OPTIONS, [], undefined, { getOnInit: true })
export const janThemesPathAtom = atomWithStorage<string | undefined>(
THEME_PATH,
undefined,
undefined,
{ getOnInit: true }
)

export const selectedThemeIdAtom = atomWithStorage<string>(
THEME,
'',
Expand Down
30 changes: 25 additions & 5 deletions web/helpers/atoms/Thread.atom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,26 @@ export const waitingToSendMessage = atom<boolean | undefined>(undefined)
*/
export const isGeneratingResponseAtom = atom<boolean | undefined>(undefined)

/**
* Create a new thread and add it to the thread list
*/
export const createNewThreadAtom = atom(null, (get, set, newThread: Thread) => {
// create thread state for this new thread
const currentState = { ...get(threadStatesAtom) }

const threadState: ThreadState = {
hasMore: false,
waitingForResponse: false,
lastMessage: undefined,
}
currentState[newThread.id] = threadState
set(threadStatesAtom, currentState)

// add the new thread on top of the thread list to the state
const threads = get(threadsAtom)
set(threadsAtom, [newThread, ...threads])
})

/**
* Remove a thread state from the atom
*/
Expand Down Expand Up @@ -180,12 +200,12 @@ export const updateThreadAtom = atom(
)

// sort new threads based on updated at
threads.sort((thread1, thread2) => {
const aDate = new Date(thread1.updated ?? 0)
const bDate = new Date(thread2.updated ?? 0)
return bDate.getTime() - aDate.getTime()
threads.sort((a, b) => {
return ((a.metadata?.updated_at as number) ?? 0) >
((b.metadata?.updated_at as number) ?? 0)
? -1
: 1
})

set(threadsAtom, threads)
}
)
Expand Down
Loading

0 comments on commit da4336c

Please sign in to comment.