Merge branch 'dev' into docs-pena-team
This commit is contained in:
commit
6844bf606d
4
.gitignore
vendored
4
.gitignore
vendored
@ -31,3 +31,7 @@ extensions/inference-nitro-extension/bin/saved-*
|
||||
extensions/inference-nitro-extension/bin/*.tar.gz
|
||||
extensions/inference-nitro-extension/bin/vulkaninfoSDK.exe
|
||||
extensions/inference-nitro-extension/bin/vulkaninfo
|
||||
|
||||
|
||||
# Turborepo
|
||||
.turbo
|
||||
@ -1,11 +1,11 @@
|
||||
---
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: API Reference
|
||||
description: >
|
||||
# Introduction
|
||||
|
||||
Jan API is compatible with the [OpenAI
|
||||
API](https://platform.openai.com/docs/api-reference).
|
||||
Jan API is compatible with the [OpenAI API](https://platform.openai.com/docs/api-reference).
|
||||
version: 0.1.8
|
||||
contact:
|
||||
name: Jan Discord
|
||||
@ -20,12 +20,12 @@ tags:
|
||||
description: List and describe the various models available in the API.
|
||||
- name: Chat
|
||||
description: >
|
||||
Given a list of messages comprising a conversation, the model will return
|
||||
a response.
|
||||
Given a list of messages comprising a conversation, the model will
|
||||
return a response.
|
||||
- name: Messages
|
||||
description: >
|
||||
Messages capture a conversation's content. This can include the content
|
||||
from LLM responses and other metadata from [chat
|
||||
Messages capture a conversation's content. This can include the
|
||||
content from LLM responses and other metadata from [chat
|
||||
completions](/specs/chats).
|
||||
- name: Threads
|
||||
- name: Assistants
|
||||
@ -49,16 +49,16 @@ paths:
|
||||
summary: |
|
||||
Create chat completion
|
||||
description: >
|
||||
Creates a model response for the given chat conversation. <a href =
|
||||
"https://platform.openai.com/docs/api-reference/chat/create"> Equivalent
|
||||
to OpenAI's create chat completion. </a>
|
||||
Creates a model response for the given chat conversation. <a href
|
||||
= "https://platform.openai.com/docs/api-reference/chat/create">
|
||||
Equivalent to OpenAI's create chat completion. </a>
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: specs/chat.yaml#/components/schemas/ChatCompletionRequest
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
@ -192,9 +192,7 @@ paths:
|
||||
}
|
||||
|
||||
|
||||
response =
|
||||
requests.post('http://localhost:1337/v1/chat/completions',
|
||||
json=data)
|
||||
response = requests.post('http://localhost:1337/v1/chat/completions', json=data)
|
||||
|
||||
print(response.json())
|
||||
/models:
|
||||
@ -204,12 +202,12 @@ paths:
|
||||
- Models
|
||||
summary: List models
|
||||
description: >
|
||||
Lists the currently available models, and provides basic information
|
||||
about each one such as the owner and availability. <a href =
|
||||
"https://platform.openai.com/docs/api-reference/models/list"> Equivalent
|
||||
to OpenAI's list model. </a>
|
||||
Lists the currently available models, and provides basic
|
||||
information about each one such as the owner and availability. <a href
|
||||
= "https://platform.openai.com/docs/api-reference/models/list">
|
||||
Equivalent to OpenAI's list model. </a>
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
@ -228,14 +226,6 @@ paths:
|
||||
headers: {Accept: 'application/json'}
|
||||
});
|
||||
const data = await response.json();
|
||||
- lang: Python
|
||||
source: |-
|
||||
import requests
|
||||
|
||||
url = 'http://localhost:1337/v1/models'
|
||||
headers = {'Accept': 'application/json'}
|
||||
response = requests.get(url, headers=headers)
|
||||
data = response.json()
|
||||
- lang: Node.js
|
||||
source: |-
|
||||
const fetch = require('node-fetch');
|
||||
@ -249,7 +239,15 @@ paths:
|
||||
fetch(url, options)
|
||||
.then(res => res.json())
|
||||
.then(json => console.log(json));
|
||||
/models/download/{model_id}:
|
||||
- lang: Python
|
||||
source: |-
|
||||
import requests
|
||||
|
||||
url = 'http://localhost:1337/v1/models'
|
||||
headers = {'Accept': 'application/json'}
|
||||
response = requests.get(url, headers=headers)
|
||||
data = response.json()
|
||||
"/models/download/{model_id}":
|
||||
get:
|
||||
operationId: downloadModel
|
||||
tags:
|
||||
@ -267,7 +265,7 @@ paths:
|
||||
description: |
|
||||
The ID of the model to use for this request.
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
@ -304,20 +302,18 @@ paths:
|
||||
import requests
|
||||
|
||||
|
||||
response =
|
||||
requests.get('http://localhost:1337/v1/models/download/{model_id}',
|
||||
headers={'accept': 'application/json'})
|
||||
response = requests.get('http://localhost:1337/v1/models/download/{model_id}', headers={'accept': 'application/json'})
|
||||
|
||||
data = response.json()
|
||||
/models/{model_id}:
|
||||
"/models/{model_id}":
|
||||
get:
|
||||
operationId: retrieveModel
|
||||
tags:
|
||||
- Models
|
||||
summary: Retrieve model
|
||||
description: >
|
||||
Get a model instance, providing basic information about the model such
|
||||
as the owner and permissioning. <a href =
|
||||
Get a model instance, providing basic information about the model
|
||||
such as the owner and permissioning. <a href =
|
||||
"https://platform.openai.com/docs/api-reference/models/retrieve">
|
||||
Equivalent to OpenAI's retrieve model. </a>
|
||||
parameters:
|
||||
@ -330,7 +326,7 @@ paths:
|
||||
description: |
|
||||
The ID of the model to use for this request.
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
@ -374,9 +370,7 @@ paths:
|
||||
model_id = 'mistral-ins-7b-q4'
|
||||
|
||||
|
||||
response =
|
||||
requests.get(f'http://localhost:1337/v1/models/{model_id}',
|
||||
headers={'accept': 'application/json'})
|
||||
response = requests.get(f'http://localhost:1337/v1/models/{model_id}', headers={'accept': 'application/json'})
|
||||
|
||||
print(response.json())
|
||||
delete:
|
||||
@ -398,7 +392,7 @@ paths:
|
||||
description: |
|
||||
The model id to delete
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
@ -442,9 +436,7 @@ paths:
|
||||
model_id = 'mistral-ins-7b-q4'
|
||||
|
||||
|
||||
response =
|
||||
requests.delete(f'http://localhost:1337/v1/models/{model_id}',
|
||||
headers={'accept': 'application/json'})
|
||||
response = requests.delete(f'http://localhost:1337/v1/models/{model_id}', headers={'accept': 'application/json'})
|
||||
/threads:
|
||||
post:
|
||||
operationId: createThread
|
||||
@ -462,7 +454,7 @@ paths:
|
||||
schema:
|
||||
$ref: specs/threads.yaml#/components/schemas/CreateThreadObject
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: Thread created successfully
|
||||
content:
|
||||
application/json:
|
||||
@ -483,6 +475,73 @@ paths:
|
||||
"content": "How does AI work? Explain it in simple terms."
|
||||
}]
|
||||
}'
|
||||
- lang: JavaScript
|
||||
source: |-
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
fetch('http://localhost:1337/v1/threads', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Hello, what is AI?',
|
||||
file_ids: ['file-abc123']
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: 'How does AI work? Explain it in simple terms.'
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
- lang: Node.js
|
||||
source: |-
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
fetch('http://localhost:1337/v1/threads', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Hello, what is AI?',
|
||||
file_ids: ['file-abc123']
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: 'How does AI work? Explain it in simple terms.'
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
- lang: Python
|
||||
source: |-
|
||||
import requests
|
||||
|
||||
url = 'http://localhost:1337/v1/threads'
|
||||
payload = {
|
||||
'messages': [
|
||||
{
|
||||
'role': 'user',
|
||||
'content': 'Hello, what is AI?',
|
||||
'file_ids': ['file-abc123']
|
||||
},
|
||||
{
|
||||
'role': 'user',
|
||||
'content': 'How does AI work? Explain it in simple terms.'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
response = requests.post(url, json=payload)
|
||||
print(response.text)
|
||||
get:
|
||||
operationId: listThreads
|
||||
tags:
|
||||
@ -491,7 +550,7 @@ paths:
|
||||
description: |
|
||||
Retrieves a list of all threads available in the system.
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: List of threads retrieved successfully
|
||||
content:
|
||||
application/json:
|
||||
@ -516,10 +575,37 @@ paths:
|
||||
metadata: {}
|
||||
x-codeSamples:
|
||||
- lang: cURL
|
||||
source: |
|
||||
source: |-
|
||||
curl http://localhost:1337/v1/threads \
|
||||
-H "Content-Type: application/json" \
|
||||
/threads/{thread_id}:
|
||||
-H "Content-Type: application/json"
|
||||
- lang: JavaScript
|
||||
source: |-
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
fetch('http://localhost:1337/v1/threads', {
|
||||
method: 'GET',
|
||||
headers: {'Content-Type': 'application/json'}
|
||||
}).then(res => res.json())
|
||||
.then(json => console.log(json));
|
||||
- lang: Node.js
|
||||
source: |-
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
fetch('http://localhost:1337/v1/threads', {
|
||||
method: 'GET',
|
||||
headers: {'Content-Type': 'application/json'}
|
||||
}).then(res => res.json())
|
||||
.then(json => console.log(json));
|
||||
- lang: Python
|
||||
source: |-
|
||||
import requests
|
||||
|
||||
url = 'http://localhost:1337/v1/threads'
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
print(response.json())
|
||||
"/threads/{thread_id}":
|
||||
get:
|
||||
operationId: getThread
|
||||
tags:
|
||||
@ -539,7 +625,7 @@ paths:
|
||||
description: |
|
||||
The ID of the thread to retrieve.
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: Thread details retrieved successfully
|
||||
content:
|
||||
application/json:
|
||||
@ -579,7 +665,7 @@ paths:
|
||||
items:
|
||||
$ref: specs/threads.yaml#/components/schemas/ThreadMessageObject
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: Thread modified successfully
|
||||
content:
|
||||
application/json:
|
||||
@ -618,7 +704,7 @@ paths:
|
||||
description: |
|
||||
The ID of the thread to be deleted.
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: Thread deleted successfully
|
||||
content:
|
||||
application/json:
|
||||
@ -639,7 +725,7 @@ paths:
|
||||
"https://platform.openai.com/docs/api-reference/assistants/listAssistants">
|
||||
Equivalent to OpenAI's list assistants. </a>
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: List of assistants retrieved successfully
|
||||
content:
|
||||
application/json:
|
||||
@ -676,10 +762,36 @@ paths:
|
||||
metadata: {}
|
||||
x-codeSamples:
|
||||
- lang: cURL
|
||||
source: |
|
||||
source: |-
|
||||
curl http://localhost:1337/v1/assistants \
|
||||
-H "Content-Type: application/json" \
|
||||
/assistants/{assistant_id}:
|
||||
-H "Content-Type: application/json"
|
||||
- lang: JavaScript
|
||||
source: |-
|
||||
fetch('http://localhost:1337/v1/assistants', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
- lang: Node.js
|
||||
source: |-
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
fetch('http://localhost:1337/v1/assistants', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
- lang: Python
|
||||
source: |-
|
||||
import requests
|
||||
|
||||
url = 'http://localhost:1337/v1/assistants'
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
"/assistants/{assistant_id}":
|
||||
get:
|
||||
operationId: getAssistant
|
||||
tags:
|
||||
@ -699,19 +811,51 @@ paths:
|
||||
description: |
|
||||
The ID of the assistant to retrieve.
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: null
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: >-
|
||||
specs/assistants.yaml#/components/schemas/RetrieveAssistantResponse
|
||||
$ref: specs/assistants.yaml#/components/schemas/RetrieveAssistantResponse
|
||||
x-codeSamples:
|
||||
- lang: cURL
|
||||
source: |
|
||||
source: |-
|
||||
curl http://localhost:1337/v1/assistants/{assistant_id} \
|
||||
-H "Content-Type: application/json" \
|
||||
/threads/{thread_id}/messages:
|
||||
-H "Content-Type: application/json"
|
||||
- lang: JavaScript
|
||||
source: |-
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
let assistantId = 'abc123';
|
||||
|
||||
fetch(`http://localhost:1337/v1/assistants/${assistantId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
- lang: Node.js
|
||||
source: |-
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
let assistantId = 'abc123';
|
||||
|
||||
fetch(`http://localhost:1337/v1/assistants/${assistantId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
- lang: Python
|
||||
source: >-
|
||||
import requests
|
||||
|
||||
|
||||
assistant_id = 'abc123'
|
||||
|
||||
|
||||
response = requests.get(f'http://localhost:1337/v1/assistants/{assistant_id}', headers={'Content-Type': 'application/json'})
|
||||
"/threads/{thread_id}/messages":
|
||||
get:
|
||||
operationId: listMessages
|
||||
tags:
|
||||
@ -730,7 +874,7 @@ paths:
|
||||
description: |
|
||||
The ID of the thread from which to retrieve messages.
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: List of messages retrieved successfully
|
||||
content:
|
||||
application/json:
|
||||
@ -782,7 +926,7 @@ paths:
|
||||
- role
|
||||
- content
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: Message created successfully
|
||||
content:
|
||||
application/json:
|
||||
@ -797,7 +941,7 @@ paths:
|
||||
"role": "user",
|
||||
"content": "How does AI work? Explain it in simple terms."
|
||||
}'
|
||||
/threads/{thread_id}/messages/{message_id}:
|
||||
"/threads/{thread_id}/messages/{message_id}":
|
||||
get:
|
||||
operationId: retrieveMessage
|
||||
tags:
|
||||
@ -824,7 +968,7 @@ paths:
|
||||
description: |
|
||||
The ID of the message to retrieve.
|
||||
responses:
|
||||
'200':
|
||||
"200":
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
@ -833,8 +977,8 @@ paths:
|
||||
x-codeSamples:
|
||||
- lang: cURL
|
||||
source: >
|
||||
curl
|
||||
http://localhost:1337/v1/threads/{thread_id}/messages/{message_id} \
|
||||
curl http://localhost:1337/v1/threads/{thread_id}/messages/{message_id}
|
||||
\
|
||||
-H "Content-Type: application/json"
|
||||
x-webhooks:
|
||||
ModelObject:
|
||||
@ -856,9 +1000,10 @@ x-webhooks:
|
||||
post:
|
||||
summary: The assistant object
|
||||
description: >
|
||||
Build assistants that can call models and use tools to perform tasks.
|
||||
<a href = "https://platform.openai.com/docs/api-reference/assistants">
|
||||
Equivalent to OpenAI's assistants object. </a>
|
||||
Build assistants that can call models and use tools to perform
|
||||
tasks. <a href =
|
||||
"https://platform.openai.com/docs/api-reference/assistants"> Equivalent
|
||||
to OpenAI's assistants object. </a>
|
||||
operationId: AssistantObjects
|
||||
tags:
|
||||
- Assistants
|
||||
@ -885,8 +1030,7 @@ x-webhooks:
|
||||
ThreadObject:
|
||||
post:
|
||||
summary: The thread object
|
||||
description: >-
|
||||
Represents a thread that contains messages. <a href =
|
||||
description: Represents a thread that contains messages. <a href =
|
||||
"https://platform.openai.com/docs/api-reference/threads/object">
|
||||
Equivalent to OpenAI's thread object. </a>
|
||||
operationId: ThreadObject
|
||||
|
||||
@ -93,12 +93,12 @@ export function handleAppIPCs() {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, {
|
||||
title: 'Select model files',
|
||||
buttonLabel: 'Select',
|
||||
properties: ['openFile', 'multiSelections'],
|
||||
properties: ['openFile', 'openDirectory', 'multiSelections'],
|
||||
})
|
||||
if (canceled) {
|
||||
return
|
||||
} else {
|
||||
return filePaths
|
||||
}
|
||||
|
||||
return filePaths
|
||||
})
|
||||
}
|
||||
|
||||
@ -61,6 +61,8 @@
|
||||
"test:e2e": "playwright test --workers=1",
|
||||
"copy:assets": "rimraf --glob \"./pre-install/*.tgz\" && cpx \"../pre-install/*.tgz\" \"./pre-install\"",
|
||||
"dev": "yarn copy:assets && tsc -p . && electron .",
|
||||
"compile": "tsc -p .",
|
||||
"start": "electron .",
|
||||
"build": "yarn copy:assets && run-script-os",
|
||||
"build:test": "yarn copy:assets && run-script-os",
|
||||
"build:test:darwin": "tsc -p . && electron-builder -p never -m --dir",
|
||||
|
||||
@ -41,7 +41,8 @@
|
||||
"build:extensions": "run-script-os",
|
||||
"build:test": "yarn copy:assets && yarn build:web && yarn workspace jan build:test",
|
||||
"build": "yarn build:web && yarn build:electron",
|
||||
"build:publish": "yarn copy:assets && yarn build:web && yarn workspace jan build:publish"
|
||||
"build:publish": "yarn copy:assets && yarn build:web && yarn workspace jan build:publish",
|
||||
"turbo:electron": "turbo run dev --parallel --filter=!@janhq/server"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^8.2.1",
|
||||
|
||||
29
turbo.json
Normal file
29
turbo.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "https://turbo.build/schema.json",
|
||||
"pipeline": {
|
||||
"build": {
|
||||
"outputs": [".next/**", "!.next/cache/**"]
|
||||
},
|
||||
"dev": {
|
||||
"cache": false
|
||||
},
|
||||
"web#build": {
|
||||
"dependsOn": ["@janhq/core#build"]
|
||||
},
|
||||
"web:dev": {
|
||||
"cache": false,
|
||||
"persistent": true,
|
||||
"dependsOn": ["@janhq/core#build", "@janhq/uikit#build"]
|
||||
},
|
||||
"electron:dev": {
|
||||
"cache": false,
|
||||
"persistent": true,
|
||||
"dependsOn": ["@janhq/core#build", "@janhq/server#build", "jan#compile"]
|
||||
},
|
||||
"electron#build": {
|
||||
"dependsOn": ["web#build", "server#build", "core#build"],
|
||||
"cache": false
|
||||
},
|
||||
"type-check": {}
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
type Props = {
|
||||
title: string
|
||||
description?: string
|
||||
disabled?: boolean
|
||||
onChange?: (text?: string) => void
|
||||
}
|
||||
|
||||
export default function ItemCardSidebar({
|
||||
description,
|
||||
title,
|
||||
disabled,
|
||||
onChange,
|
||||
}: Props) {
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span>{title}</span>
|
||||
</div>
|
||||
<input
|
||||
value={description}
|
||||
disabled={disabled}
|
||||
type="text"
|
||||
className="block w-full rounded-md border-0 px-1 py-1.5 text-white shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
|
||||
placeholder=""
|
||||
onChange={(e) => onChange?.(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -11,8 +11,6 @@ import EventListenerWrapper from '@/containers/Providers/EventListener'
|
||||
import JotaiWrapper from '@/containers/Providers/Jotai'
|
||||
import ThemeWrapper from '@/containers/Providers/Theme'
|
||||
|
||||
import FeatureToggleWrapper from '@/context/FeatureToggle'
|
||||
|
||||
import { setupCoreServices } from '@/services/coreService'
|
||||
import {
|
||||
isCoreExtensionInstalled,
|
||||
@ -81,7 +79,6 @@ const Providers = (props: PropsWithChildren) => {
|
||||
{settingUp && <Loader description="Preparing Update..." />}
|
||||
{setupCore && activated && (
|
||||
<KeyListener>
|
||||
<FeatureToggleWrapper>
|
||||
<EventListenerWrapper>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<DataLoader>{children}</DataLoader>
|
||||
@ -89,7 +86,6 @@ const Providers = (props: PropsWithChildren) => {
|
||||
{!isMac && <GPUDriverPrompt />}
|
||||
</EventListenerWrapper>
|
||||
<Toaster />
|
||||
</FeatureToggleWrapper>
|
||||
</KeyListener>
|
||||
)}
|
||||
</ThemeWrapper>
|
||||
|
||||
@ -1,104 +0,0 @@
|
||||
import { createContext, ReactNode, useEffect, useState } from 'react'
|
||||
|
||||
interface FeatureToggleContextType {
|
||||
experimentalFeature: boolean
|
||||
ignoreSSL: boolean
|
||||
proxy: string
|
||||
proxyEnabled: boolean
|
||||
vulkanEnabled: boolean
|
||||
setExperimentalFeature: (on: boolean) => void
|
||||
setVulkanEnabled: (on: boolean) => void
|
||||
setIgnoreSSL: (on: boolean) => void
|
||||
setProxy: (value: string) => void
|
||||
setProxyEnabled: (on: boolean) => void
|
||||
}
|
||||
|
||||
const initialContext: FeatureToggleContextType = {
|
||||
experimentalFeature: false,
|
||||
ignoreSSL: false,
|
||||
proxy: '',
|
||||
proxyEnabled: false,
|
||||
vulkanEnabled: false,
|
||||
setExperimentalFeature: () => {},
|
||||
setVulkanEnabled: () => {},
|
||||
setIgnoreSSL: () => {},
|
||||
setProxy: () => {},
|
||||
setProxyEnabled: () => {},
|
||||
}
|
||||
|
||||
export const FeatureToggleContext =
|
||||
createContext<FeatureToggleContextType>(initialContext)
|
||||
|
||||
export default function FeatureToggleWrapper({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode
|
||||
}) {
|
||||
const EXPERIMENTAL_FEATURE = 'experimentalFeature'
|
||||
const VULKAN_ENABLED = 'vulkanEnabled'
|
||||
const IGNORE_SSL = 'ignoreSSLFeature'
|
||||
const HTTPS_PROXY_FEATURE = 'httpsProxyFeature'
|
||||
const PROXY_FEATURE_ENABLED = 'proxyFeatureEnabled'
|
||||
|
||||
const [experimentalFeature, directSetExperimentalFeature] =
|
||||
useState<boolean>(false)
|
||||
const [proxyEnabled, directSetProxyEnabled] = useState<boolean>(false)
|
||||
const [vulkanEnabled, directEnableVulkan] = useState<boolean>(false)
|
||||
const [ignoreSSL, directSetIgnoreSSL] = useState<boolean>(false)
|
||||
const [proxy, directSetProxy] = useState<string>('')
|
||||
|
||||
useEffect(() => {
|
||||
directSetExperimentalFeature(
|
||||
localStorage.getItem(EXPERIMENTAL_FEATURE) === 'true'
|
||||
)
|
||||
directSetIgnoreSSL(localStorage.getItem(IGNORE_SSL) === 'true')
|
||||
directSetProxy(localStorage.getItem(HTTPS_PROXY_FEATURE) ?? '')
|
||||
directSetProxyEnabled(
|
||||
localStorage.getItem(PROXY_FEATURE_ENABLED) === 'true'
|
||||
)
|
||||
}, [])
|
||||
|
||||
const setExperimentalFeature = (on: boolean) => {
|
||||
localStorage.setItem(EXPERIMENTAL_FEATURE, on ? 'true' : 'false')
|
||||
directSetExperimentalFeature(on)
|
||||
}
|
||||
|
||||
const setVulkanEnabled = (on: boolean) => {
|
||||
localStorage.setItem(VULKAN_ENABLED, on ? 'true' : 'false')
|
||||
directEnableVulkan(on)
|
||||
}
|
||||
|
||||
const setIgnoreSSL = (on: boolean) => {
|
||||
localStorage.setItem(IGNORE_SSL, on ? 'true' : 'false')
|
||||
directSetIgnoreSSL(on)
|
||||
}
|
||||
|
||||
const setProxy = (proxy: string) => {
|
||||
localStorage.setItem(HTTPS_PROXY_FEATURE, proxy)
|
||||
directSetProxy(proxy)
|
||||
}
|
||||
|
||||
const setProxyEnabled = (on: boolean) => {
|
||||
localStorage.setItem(PROXY_FEATURE_ENABLED, on ? 'true' : 'false')
|
||||
directSetProxyEnabled(on)
|
||||
}
|
||||
|
||||
return (
|
||||
<FeatureToggleContext.Provider
|
||||
value={{
|
||||
experimentalFeature,
|
||||
ignoreSSL,
|
||||
proxy,
|
||||
proxyEnabled,
|
||||
vulkanEnabled,
|
||||
setExperimentalFeature,
|
||||
setVulkanEnabled,
|
||||
setIgnoreSSL,
|
||||
setProxy,
|
||||
setProxyEnabled,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</FeatureToggleContext.Provider>
|
||||
)
|
||||
}
|
||||
16
web/helpers/atoms/ApiServer.atom.ts
Normal file
16
web/helpers/atoms/ApiServer.atom.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { atomWithStorage } from 'jotai/utils'
|
||||
|
||||
export const hostOptions = ['127.0.0.1', '0.0.0.0']
|
||||
|
||||
export const apiServerPortAtom = atomWithStorage('apiServerPort', '1337')
|
||||
export const apiServerHostAtom = atomWithStorage('apiServerHost', '127.0.0.1')
|
||||
|
||||
export const apiServerCorsEnabledAtom = atomWithStorage(
|
||||
'apiServerCorsEnabled',
|
||||
true
|
||||
)
|
||||
|
||||
export const apiServerVerboseLogEnabledAtom = atomWithStorage(
|
||||
'apiServerVerboseLogEnabled',
|
||||
true
|
||||
)
|
||||
@ -1,3 +1,21 @@
|
||||
import { atom } from 'jotai'
|
||||
import { atomWithStorage } from 'jotai/utils'
|
||||
|
||||
const EXPERIMENTAL_FEATURE = 'experimentalFeature'
|
||||
const PROXY_FEATURE_ENABLED = 'proxyFeatureEnabled'
|
||||
const VULKAN_ENABLED = 'vulkanEnabled'
|
||||
const IGNORE_SSL = 'ignoreSSLFeature'
|
||||
const HTTPS_PROXY_FEATURE = 'httpsProxyFeature'
|
||||
|
||||
export const janDataFolderPathAtom = atom('')
|
||||
|
||||
export const experimentalFeatureEnabledAtom = atomWithStorage(
|
||||
EXPERIMENTAL_FEATURE,
|
||||
false
|
||||
)
|
||||
|
||||
export const proxyEnabledAtom = atomWithStorage(PROXY_FEATURE_ENABLED, false)
|
||||
export const proxyAtom = atomWithStorage(HTTPS_PROXY_FEATURE, '')
|
||||
|
||||
export const ignoreSslAtom = atomWithStorage(IGNORE_SSL, false)
|
||||
export const vulkanEnabledAtom = atomWithStorage(VULKAN_ENABLED, false)
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { useContext } from 'react'
|
||||
|
||||
import {
|
||||
ExtensionTypeEnum,
|
||||
HuggingFaceExtension,
|
||||
@ -7,18 +5,18 @@ import {
|
||||
Quantization,
|
||||
} from '@janhq/core'
|
||||
|
||||
import { useSetAtom } from 'jotai'
|
||||
|
||||
import { FeatureToggleContext } from '@/context/FeatureToggle'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
|
||||
import { extensionManager } from '@/extension/ExtensionManager'
|
||||
import { ignoreSslAtom, proxyAtom } from '@/helpers/atoms/AppConfig.atom'
|
||||
import {
|
||||
conversionStatusAtom,
|
||||
conversionErrorAtom,
|
||||
} from '@/helpers/atoms/HFConverter.atom'
|
||||
|
||||
export const useConvertHuggingFaceModel = () => {
|
||||
const { ignoreSSL, proxy } = useContext(FeatureToggleContext)
|
||||
const proxy = useAtomValue(proxyAtom)
|
||||
const ignoreSSL = useAtomValue(ignoreSslAtom)
|
||||
const setConversionStatus = useSetAtom(conversionStatusAtom)
|
||||
const setConversionError = useSetAtom(conversionErrorAtom)
|
||||
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { useContext } from 'react'
|
||||
|
||||
import {
|
||||
Assistant,
|
||||
ConversationalExtension,
|
||||
@ -17,8 +15,6 @@ import { atom, useAtomValue, useSetAtom } from 'jotai'
|
||||
import { selectedModelAtom } from '@/containers/DropdownListSidebar'
|
||||
import { fileUploadAtom } from '@/containers/Providers/Jotai'
|
||||
|
||||
import { FeatureToggleContext } from '@/context/FeatureToggle'
|
||||
|
||||
import { generateThreadId } from '@/utils/thread'
|
||||
|
||||
import useRecommendedModel from './useRecommendedModel'
|
||||
@ -27,6 +23,7 @@ import useSetActiveThread from './useSetActiveThread'
|
||||
|
||||
import { extensionManager } from '@/extension'
|
||||
|
||||
import { experimentalFeatureEnabledAtom } from '@/helpers/atoms/AppConfig.atom'
|
||||
import {
|
||||
threadsAtom,
|
||||
threadStatesAtom,
|
||||
@ -59,7 +56,8 @@ export const useCreateNewThread = () => {
|
||||
const setFileUpload = useSetAtom(fileUploadAtom)
|
||||
const setSelectedModel = useSetAtom(selectedModelAtom)
|
||||
const setThreadModelParams = useSetAtom(setThreadModelParamsAtom)
|
||||
const { experimentalFeature } = useContext(FeatureToggleContext)
|
||||
|
||||
const experimentalEnabled = useAtomValue(experimentalFeatureEnabledAtom)
|
||||
const setIsGeneratingResponse = useSetAtom(isGeneratingResponseAtom)
|
||||
|
||||
const { recommendedModel, downloadedModels } = useRecommendedModel()
|
||||
@ -94,7 +92,7 @@ export const useCreateNewThread = () => {
|
||||
const assistantInfo: ThreadAssistantInfo = {
|
||||
assistant_id: assistant.id,
|
||||
assistant_name: assistant.name,
|
||||
tools: experimentalFeature ? [assistantTools] : assistant.tools,
|
||||
tools: experimentalEnabled ? [assistantTools] : assistant.tools,
|
||||
model: {
|
||||
id: defaultModel?.id ?? '*',
|
||||
settings: defaultModel?.settings ?? {},
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useCallback, useContext } from 'react'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import {
|
||||
Model,
|
||||
@ -10,17 +10,22 @@ import {
|
||||
DownloadState,
|
||||
} from '@janhq/core'
|
||||
|
||||
import { useSetAtom } from 'jotai'
|
||||
|
||||
import { FeatureToggleContext } from '@/context/FeatureToggle'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
|
||||
import { setDownloadStateAtom } from './useDownloadState'
|
||||
|
||||
import { extensionManager } from '@/extension/ExtensionManager'
|
||||
import {
|
||||
ignoreSslAtom,
|
||||
proxyAtom,
|
||||
proxyEnabledAtom,
|
||||
} from '@/helpers/atoms/AppConfig.atom'
|
||||
import { addDownloadingModelAtom } from '@/helpers/atoms/Model.atom'
|
||||
|
||||
export default function useDownloadModel() {
|
||||
const { ignoreSSL, proxy, proxyEnabled } = useContext(FeatureToggleContext)
|
||||
const ignoreSSL = useAtomValue(ignoreSslAtom)
|
||||
const proxy = useAtomValue(proxyAtom)
|
||||
const proxyEnabled = useAtomValue(proxyEnabledAtom)
|
||||
const setDownloadState = useSetAtom(setDownloadStateAtom)
|
||||
const addDownloadingModel = useSetAtom(addDownloadingModelAtom)
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ export default function useDropModelBinaries() {
|
||||
}))
|
||||
if (unsupportedFiles.length > 0) {
|
||||
snackbar({
|
||||
description: `File has to be a .gguf file`,
|
||||
description: `Only files with .gguf extension can be imported.`,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
|
||||
@ -20,7 +20,9 @@ export const useGetHFRepoData = () => {
|
||||
const data = await res.json()
|
||||
setRepoData(data)
|
||||
} catch (err) {
|
||||
setFetchError(err as Error)
|
||||
setFetchError(
|
||||
Error("The repo does not exist or you don't have access to it.")
|
||||
)
|
||||
}
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { useContext, useEffect, useRef, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { InferenceEvent, MessageStatus, events } from '@janhq/core'
|
||||
|
||||
@ -24,8 +24,6 @@ import { twMerge } from 'tailwind-merge'
|
||||
|
||||
import { currentPromptAtom, fileUploadAtom } from '@/containers/Providers/Jotai'
|
||||
|
||||
import { FeatureToggleContext } from '@/context/FeatureToggle'
|
||||
|
||||
import { useActiveModel } from '@/hooks/useActiveModel'
|
||||
import { useClickOutside } from '@/hooks/useClickOutside'
|
||||
|
||||
@ -34,6 +32,7 @@ import useSendChatMessage from '@/hooks/useSendChatMessage'
|
||||
import FileUploadPreview from '../FileUploadPreview'
|
||||
import ImageUploadPreview from '../ImageUploadPreview'
|
||||
|
||||
import { experimentalFeatureEnabledAtom } from '@/helpers/atoms/AppConfig.atom'
|
||||
import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom'
|
||||
import {
|
||||
activeThreadAtom,
|
||||
@ -58,7 +57,7 @@ const ChatInput: React.FC = () => {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
const imageInputRef = useRef<HTMLInputElement>(null)
|
||||
const [showAttacmentMenus, setShowAttacmentMenus] = useState(false)
|
||||
const { experimentalFeature } = useContext(FeatureToggleContext)
|
||||
const experimentalFeature = useAtomValue(experimentalFeatureEnabledAtom)
|
||||
const isGeneratingResponse = useAtomValue(isGeneratingResponseAtom)
|
||||
const threadStates = useAtomValue(threadStatesAtom)
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import React, { useContext } from 'react'
|
||||
import React from 'react'
|
||||
|
||||
import {
|
||||
Input,
|
||||
@ -24,8 +23,6 @@ import DropdownListSidebar, {
|
||||
selectedModelAtom,
|
||||
} from '@/containers/DropdownListSidebar'
|
||||
|
||||
import { FeatureToggleContext } from '@/context/FeatureToggle'
|
||||
|
||||
import { useCreateNewThread } from '@/hooks/useCreateNewThread'
|
||||
|
||||
import { getConfigurationsData } from '@/utils/componentSettings'
|
||||
@ -37,6 +34,7 @@ import ModelSetting from '../ModelSetting'
|
||||
|
||||
import SettingComponentBuilder from '../ModelSetting/SettingComponent'
|
||||
|
||||
import { experimentalFeatureEnabledAtom } from '@/helpers/atoms/AppConfig.atom'
|
||||
import {
|
||||
activeThreadAtom,
|
||||
getActiveThreadModelParamsAtom,
|
||||
@ -50,7 +48,7 @@ const Sidebar: React.FC = () => {
|
||||
const activeModelParams = useAtomValue(getActiveThreadModelParamsAtom)
|
||||
const selectedModel = useAtomValue(selectedModelAtom)
|
||||
const { updateThreadMetadata } = useCreateNewThread()
|
||||
const { experimentalFeature } = useContext(FeatureToggleContext)
|
||||
const experimentalFeature = useAtomValue(experimentalFeatureEnabledAtom)
|
||||
|
||||
const modelEngineParams = toSettingParams(activeModelParams)
|
||||
const modelRuntimeParams = toRuntimeParams(activeModelParams)
|
||||
@ -174,7 +172,7 @@ const Sidebar: React.FC = () => {
|
||||
<div className="px-2 py-4">
|
||||
<SettingComponentBuilder
|
||||
componentData={componentDataEngineSetting}
|
||||
selector={(x: any) => x.name === 'prompt_template'}
|
||||
selector={(x) => x.name === 'prompt_template'}
|
||||
/>
|
||||
</div>
|
||||
</CardSidebar>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import { useDropzone } from 'react-dropzone'
|
||||
|
||||
@ -18,8 +18,6 @@ import { showLeftSideBarAtom } from '@/containers/Providers/KeyListener'
|
||||
|
||||
import { snackbar } from '@/containers/Toast'
|
||||
|
||||
import { FeatureToggleContext } from '@/context/FeatureToggle'
|
||||
|
||||
import { activeModelAtom } from '@/hooks/useActiveModel'
|
||||
import { queuedMessageAtom, reloadModelAtom } from '@/hooks/useSendChatMessage'
|
||||
|
||||
@ -31,6 +29,7 @@ import ChatInput from './ChatInput'
|
||||
import RequestDownloadModel from './RequestDownloadModel'
|
||||
import Sidebar from './Sidebar'
|
||||
|
||||
import { experimentalFeatureEnabledAtom } from '@/helpers/atoms/AppConfig.atom'
|
||||
import {
|
||||
activeThreadAtom,
|
||||
engineParamsUpdateAtom,
|
||||
@ -63,7 +62,7 @@ const ChatScreen: React.FC = () => {
|
||||
const reloadModel = useAtomValue(reloadModelAtom)
|
||||
const [dragRejected, setDragRejected] = useState({ code: '' })
|
||||
const setFileUpload = useSetAtom(fileUploadAtom)
|
||||
const { experimentalFeature } = useContext(FeatureToggleContext)
|
||||
const experimentalFeature = useAtomValue(experimentalFeatureEnabledAtom)
|
||||
|
||||
const activeModel = useAtomValue(activeModelAtom)
|
||||
|
||||
|
||||
@ -53,7 +53,7 @@ export const HuggingFaceRepoDataLoadedModal = () => {
|
||||
? '❌ This model is not supported!'
|
||||
: '✅ This model is supported!'}
|
||||
</p>
|
||||
{repoData.tags.includes('gguf') ? (
|
||||
{repoData.tags?.includes('gguf') ? (
|
||||
<p>...But you can import it manually!</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@ -18,7 +18,7 @@ export const HuggingFaceSearchErrorModal = () => {
|
||||
<p className="text-2xl font-bold">Error!</p>
|
||||
<p className="text-gray-500">Fetch error</p>
|
||||
</div>
|
||||
<p>{fetchError.message}</p>
|
||||
<p className="text-center">{fetchError.message}</p>
|
||||
<Button
|
||||
onClick={getRepoData}
|
||||
className="w-full"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useCallback, useContext, useState } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
import {
|
||||
Input,
|
||||
@ -15,13 +15,12 @@ import {
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import { UploadIcon, SearchIcon } from 'lucide-react'
|
||||
|
||||
import { FeatureToggleContext } from '@/context/FeatureToggle'
|
||||
|
||||
import { setImportModelStageAtom } from '@/hooks/useImportModel'
|
||||
|
||||
import ExploreModelList from './ExploreModelList'
|
||||
import { HuggingFaceModal } from './HuggingFaceModal'
|
||||
|
||||
import { experimentalFeatureEnabledAtom } from '@/helpers/atoms/AppConfig.atom'
|
||||
import {
|
||||
configuredModelsAtom,
|
||||
downloadedModelsAtom,
|
||||
@ -38,7 +37,7 @@ const ExploreModelsScreen = () => {
|
||||
const [showHuggingFaceModal, setShowHuggingFaceModal] = useState(false)
|
||||
const setImportModelStage = useSetAtom(setImportModelStageAtom)
|
||||
|
||||
const { experimentalFeature } = useContext(FeatureToggleContext)
|
||||
const experimentalFeature = useAtomValue(experimentalFeatureEnabledAtom)
|
||||
|
||||
const filteredModels = configuredModels.filter((x) => {
|
||||
if (sortSelected === 'Downloaded') {
|
||||
|
||||
@ -20,7 +20,7 @@ import {
|
||||
SelectValue,
|
||||
} from '@janhq/uikit'
|
||||
|
||||
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai'
|
||||
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
|
||||
|
||||
import { Paintbrush, CodeIcon } from 'lucide-react'
|
||||
import { ExternalLinkIcon, InfoIcon } from 'lucide-react'
|
||||
@ -53,13 +53,15 @@ import SettingComponentBuilder from '../Chat/ModelSetting/SettingComponent'
|
||||
|
||||
import { showRightSideBarAtom } from '../Chat/Sidebar'
|
||||
|
||||
import {
|
||||
apiServerCorsEnabledAtom,
|
||||
apiServerHostAtom,
|
||||
apiServerPortAtom,
|
||||
apiServerVerboseLogEnabledAtom,
|
||||
hostOptions,
|
||||
} from '@/helpers/atoms/ApiServer.atom'
|
||||
import { serverEnabledAtom } from '@/helpers/atoms/LocalServer.atom'
|
||||
|
||||
const corsEnabledAtom = atom(true)
|
||||
const verboseEnabledAtom = atom(true)
|
||||
const hostAtom = atom('127.0.0.1')
|
||||
const portAtom = atom('1337')
|
||||
|
||||
const LocalServerScreen = () => {
|
||||
const [errorRangePort, setErrorRangePort] = useState(false)
|
||||
const [serverEnabled, setServerEnabled] = useAtom(serverEnabledAtom)
|
||||
@ -73,14 +75,14 @@ const LocalServerScreen = () => {
|
||||
const modelEngineParams = toSettingParams(selectedModel?.settings)
|
||||
const componentDataEngineSetting = getConfigurationsData(modelEngineParams)
|
||||
|
||||
const [isCorsEnabled, setIsCorsEnabled] = useAtom(corsEnabledAtom)
|
||||
const [isVerboseEnabled, setIsVerboseEnabled] = useAtom(verboseEnabledAtom)
|
||||
const [host, setHost] = useAtom(hostAtom)
|
||||
const [port, setPort] = useAtom(portAtom)
|
||||
const [isCorsEnabled, setIsCorsEnabled] = useAtom(apiServerCorsEnabledAtom)
|
||||
const [isVerboseEnabled, setIsVerboseEnabled] = useAtom(
|
||||
apiServerVerboseLogEnabledAtom
|
||||
)
|
||||
const [host, setHost] = useAtom(apiServerHostAtom)
|
||||
const [port, setPort] = useAtom(apiServerPortAtom)
|
||||
const [loadModelError, setLoadModelError] = useAtom(loadModelErrorAtom)
|
||||
|
||||
const hostOptions = ['127.0.0.1', '0.0.0.0']
|
||||
|
||||
const FIRST_TIME_VISIT_API_SERVER = 'firstTimeVisitAPIServer'
|
||||
|
||||
const [firstTimeVisitAPIServer, setFirstTimeVisitAPIServer] =
|
||||
@ -88,11 +90,7 @@ const LocalServerScreen = () => {
|
||||
|
||||
const handleChangePort = useCallback(
|
||||
(value: string) => {
|
||||
if (Number(value) <= 0 || Number(value) >= 65536) {
|
||||
setErrorRangePort(true)
|
||||
} else {
|
||||
setErrorRangePort(false)
|
||||
}
|
||||
setErrorRangePort(Number(value) <= 0 || Number(value) >= 65536)
|
||||
setPort(value)
|
||||
},
|
||||
[setPort]
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
useCallback,
|
||||
ChangeEvent,
|
||||
} from 'react'
|
||||
import { useEffect, useState, useCallback, ChangeEvent } from 'react'
|
||||
|
||||
import { openExternalUrl, fs } from '@janhq/core'
|
||||
|
||||
@ -29,20 +23,27 @@ import {
|
||||
ScrollArea,
|
||||
} from '@janhq/uikit'
|
||||
|
||||
import { useAtom } from 'jotai'
|
||||
import { AlertTriangleIcon, AlertCircleIcon } from 'lucide-react'
|
||||
|
||||
import ShortcutModal from '@/containers/ShortcutModal'
|
||||
|
||||
import { snackbar, toaster } from '@/containers/Toast'
|
||||
|
||||
import { FeatureToggleContext } from '@/context/FeatureToggle'
|
||||
|
||||
import { useActiveModel } from '@/hooks/useActiveModel'
|
||||
import { useSettings } from '@/hooks/useSettings'
|
||||
|
||||
import DataFolder from './DataFolder'
|
||||
import FactoryReset from './FactoryReset'
|
||||
|
||||
import {
|
||||
experimentalFeatureEnabledAtom,
|
||||
ignoreSslAtom,
|
||||
proxyAtom,
|
||||
proxyEnabledAtom,
|
||||
vulkanEnabledAtom,
|
||||
} from '@/helpers/atoms/AppConfig.atom'
|
||||
|
||||
type GPU = {
|
||||
id: string
|
||||
vram: number | null
|
||||
@ -50,22 +51,19 @@ type GPU = {
|
||||
}
|
||||
|
||||
const Advanced = () => {
|
||||
const {
|
||||
experimentalFeature,
|
||||
setExperimentalFeature,
|
||||
ignoreSSL,
|
||||
setIgnoreSSL,
|
||||
proxy,
|
||||
setProxy,
|
||||
proxyEnabled,
|
||||
setProxyEnabled,
|
||||
vulkanEnabled,
|
||||
setVulkanEnabled,
|
||||
} = useContext(FeatureToggleContext)
|
||||
const [experimentalEnabled, setExperimentalEnabled] = useAtom(
|
||||
experimentalFeatureEnabledAtom
|
||||
)
|
||||
const [vulkanEnabled, setVulkanEnabled] = useAtom(vulkanEnabledAtom)
|
||||
const [proxyEnabled, setProxyEnabled] = useAtom(proxyEnabledAtom)
|
||||
const [proxy, setProxy] = useAtom(proxyAtom)
|
||||
const [ignoreSSL, setIgnoreSSL] = useAtom(ignoreSslAtom)
|
||||
|
||||
const [partialProxy, setPartialProxy] = useState<string>(proxy)
|
||||
const [gpuEnabled, setGpuEnabled] = useState<boolean>(false)
|
||||
const [gpuList, setGpuList] = useState<GPU[]>([])
|
||||
const [gpusInUse, setGpusInUse] = useState<string[]>([])
|
||||
|
||||
const { readSettings, saveSettings, validateSettings, setShowNotification } =
|
||||
useSettings()
|
||||
const { stopModel } = useActiveModel()
|
||||
@ -169,8 +167,8 @@ const Advanced = () => {
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={experimentalFeature}
|
||||
onCheckedChange={setExperimentalFeature}
|
||||
checked={experimentalEnabled}
|
||||
onCheckedChange={setExperimentalEnabled}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -355,7 +353,7 @@ const Advanced = () => {
|
||||
)}
|
||||
|
||||
{/* Vulkan for AMD GPU/ APU and Intel Arc GPU */}
|
||||
{!isMac && experimentalFeature && (
|
||||
{!isMac && experimentalEnabled && (
|
||||
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
|
||||
<div className="flex-shrink-0 space-y-1.5">
|
||||
<div className="flex gap-x-2">
|
||||
|
||||
@ -116,6 +116,11 @@ const EditModelInfoModal: React.FC = () => {
|
||||
return null
|
||||
}
|
||||
|
||||
const onTagsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const tags = e.target.value.split(',')
|
||||
setTags(tags)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={importModelStage === 'EDIT_MODEL_INFO'}
|
||||
@ -128,22 +133,24 @@ const EditModelInfoModal: React.FC = () => {
|
||||
|
||||
<div className="flex flex-row space-x-4 rounded-xl border p-4">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-blue-400">
|
||||
<Paperclip />
|
||||
<Paperclip color="#fff" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-1 flex-col">
|
||||
<p>{editingModel.name}</p>
|
||||
<div className="flex flex-row">
|
||||
<span className="mr-2 text-sm text-[#71717A]">
|
||||
{toGibibytes(editingModel.size)}
|
||||
</span>
|
||||
<div className="flex flex-row space-x-1">
|
||||
<span className="text-sm font-semibold text-[#71717A]">
|
||||
Format:{' '}
|
||||
Format:
|
||||
</span>
|
||||
<span className="text-sm font-normal text-[#71717A]">
|
||||
{editingModel.format.toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-1 flex flex-row items-center space-x-2">
|
||||
<span className="line-clamp-1 text-xs font-normal text-[#71717A]">
|
||||
{modelPath}
|
||||
@ -189,7 +196,7 @@ const EditModelInfoModal: React.FC = () => {
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<label className="mb-1">Tags</label>
|
||||
<Input />
|
||||
<Input value={tags.join(',')} onChange={onTagsChange} />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useDropzone } from 'react-dropzone'
|
||||
|
||||
import { ImportingModel, baseName, fs } from '@janhq/core'
|
||||
import { ImportingModel, baseName, fs, joinPath } from '@janhq/core'
|
||||
import { Modal, ModalContent, ModalHeader, ModalTitle } from '@janhq/uikit'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
|
||||
@ -34,14 +34,31 @@ const SelectingModelModal: React.FC = () => {
|
||||
const sanitizedFilePaths: FilePathWithSize[] = []
|
||||
for (const filePath of filePaths) {
|
||||
const fileStats = await fs.fileStat(filePath, true)
|
||||
if (!fileStats || fileStats.isDirectory) continue
|
||||
if (!fileStats) continue
|
||||
|
||||
if (!fileStats.isDirectory) {
|
||||
const fileName = await baseName(filePath)
|
||||
sanitizedFilePaths.push({
|
||||
path: filePath,
|
||||
name: fileName,
|
||||
size: fileStats.size,
|
||||
})
|
||||
} else {
|
||||
// allowing only one level of directory
|
||||
const files = await fs.readdirSync(filePath)
|
||||
|
||||
for (const file of files) {
|
||||
const fullPath = await joinPath([filePath, file])
|
||||
const fileStats = await fs.fileStat(fullPath, true)
|
||||
if (!fileStats || fileStats.isDirectory) continue
|
||||
|
||||
sanitizedFilePaths.push({
|
||||
path: fullPath,
|
||||
name: file,
|
||||
size: fileStats.size,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unsupportedFiles = sanitizedFilePaths.filter(
|
||||
@ -68,7 +85,7 @@ const SelectingModelModal: React.FC = () => {
|
||||
)
|
||||
if (unsupportedFiles.length > 0) {
|
||||
snackbar({
|
||||
description: `File has to be a .gguf file`,
|
||||
description: `Only files with .gguf extension can be imported.`,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user