Skip to content

Commit

Permalink
Merge branch 'main' into bug/showing-installed-version-plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
urmauur authored Oct 26, 2023
2 parents 929bba9 + d42b311 commit 4c1ac12
Show file tree
Hide file tree
Showing 29 changed files with 136 additions and 86 deletions.
36 changes: 36 additions & 0 deletions adr/adr-008-Extensible-Jan-with-Docker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# ADR 008: Extensible-Jan-with-Docker

## Changelog

- 2023-10-24: Initial draft

## Authors

- @vuonghoainam

## Status
Proposed

## Context

What is the issue that we're seeing that is motivating this decision or change?
- The A.I world is moving fast with multiple runtime/ prebaked environment. We or the builder cannot cover just everything but rather we should adopt it and facillitate it as much as possible within Jan.
- For `Run your own A.I`: Builder can build app on Jan (NodeJS env) and connect to external endpoint which serves the real A.I
- e.g 1: Nitro acting as proxy to `triton-inference-server` running within a Docker container controlled by Jan app
- e.g 2: Original models can be in many formats (pytorch, paddlepaddle). In order to run it with the most optimized version locally, there must be a step to transpile the model ([Ollama import model](https://github.com/jmorganca/ollama/blob/main/docs/import.md), Tensorrt). Btw Jan can prebuilt it and let user pull but later
- For `Build your own A.I`: User can fine tune model locally (of course Jan help it with remote but later)

## Decision

What is the change that we're proposing and/or doing?
- Add Docker client as Core module - [Docker node](https://github.com/apocas/dockerode)
- 2 example A.I app (adr-002) to demonstrate it and actually use!

## Consequences

What becomes easier or more difficult to do because of this change?
- We can extend limitlessly :D

## Alternatives

## Reference
7 changes: 1 addition & 6 deletions electron/tests/my-models.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@ test.afterAll(async () => {

test("shows my models", async () => {
await page.getByTestId("My Models").first().click();
const header = await page
.getByRole("heading")
.filter({ hasText: "My Models" })
.first()
.isVisible();
expect(header).toBe(false);
await page.getByTestId("testid-mymodels-header").isVisible();
// More test cases here...
});
3 changes: 2 additions & 1 deletion electron/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
"noEmitOnError": true,
"baseUrl": ".",
"allowJs": true,
"skipLibCheck": true,
"paths": { "*": ["node_modules/*"] },
"typeRoots": ["node_modules/@types"]
},
"include": ["./**/*.ts"],
"exclude": ["core", "build", "dist", "tests"]
"exclude": ["core", "build", "dist", "tests", "node_modules"]
}
5 changes: 3 additions & 2 deletions plugins/inference-plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ function requestInference(
}
subscriber.complete();
})
.catch(subscriber.error);
.catch((err) => subscriber.error(err));
});
}

Expand Down Expand Up @@ -143,7 +143,8 @@ async function handleMessageRequest(data: NewMessageRequest) {
},
error: async (err) => {
message.message =
message.message.trim() + "\n" + "Error occurred: " + err;
message.message.trim() + "\n" + "Error occurred: " + err.message;
events.emit(EventName.OnMessageResponseUpdate, message);
// TODO: Common collections should be able to access via core functions instead of store
await store.updateOne("messages", message._id, message);
},
Expand Down
70 changes: 33 additions & 37 deletions plugins/inference-plugin/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,53 +14,49 @@ const initModel = (fileName) => {
if (!fileName) {
reject("Model not found, please download again.");
}
if (subprocess) {
console.error("A subprocess is already running. Attempt to kill then reinit.");
killSubprocess();
}
resolve(fileName);
})
// Kill port process if it is already in use
.then((fileName) =>
tcpPortUsed
.waitUntilFree(PORT, 200, 3000)
.catch(() => killPortProcess(PORT))
.then(() => fileName)
)
// Spawn Nitro subprocess to load model
.then(() => {
let binaryFolder = path.join(__dirname, "nitro"); // Current directory by default
let binaryName;
return tcpPortUsed.check(PORT, "127.0.0.1").then((inUse) => {
if (!inUse) {
let binaryFolder = path.join(__dirname, "nitro"); // Current directory by default
let binaryName;

if (process.platform === "win32") {
// Todo: Need to check for CUDA support to switch between CUDA and non-CUDA binaries
binaryName = "nitro_start_windows.bat";
} else if (process.platform === "darwin") {
// Mac OS platform
binaryName = process.arch === "arm64" ? "nitro_mac_arm64" : "nitro_mac_intel";
} else {
// Linux
// Todo: Need to check for CUDA support to switch between CUDA and non-CUDA binaries
binaryName = "nitro_start_linux.sh"; // For other platforms
}
if (process.platform === "win32") {
// Todo: Need to check for CUDA support to switch between CUDA and non-CUDA binaries
binaryName = "nitro_start_windows.bat";
} else if (process.platform === "darwin") {
// Mac OS platform
binaryName =
process.arch === "arm64"
? "nitro_mac_arm64"
: "nitro_mac_intel";
} else {
// Linux
// Todo: Need to check for CUDA support to switch between CUDA and non-CUDA binaries
binaryName = "nitro_start_linux.sh"; // For other platforms
}

const binaryPath = path.join(binaryFolder, binaryName);
const binaryPath = path.join(binaryFolder, binaryName);

// Execute the binary
subprocess = spawn(binaryPath, { cwd: binaryFolder });
// Execute the binary
subprocess = spawn(binaryPath, { cwd: binaryFolder });

// Handle subprocess output
subprocess.stdout.on("data", (data) => {
console.log(`stdout: ${data}`);
});
// Handle subprocess output
subprocess.stdout.on("data", (data) => {
console.log(`stdout: ${data}`);
});

subprocess.stderr.on("data", (data) => {
console.error(`stderr: ${data}`);
});
subprocess.stderr.on("data", (data) => {
console.error(`stderr: ${data}`);
});

subprocess.on("close", (code) => {
console.log(`child process exited with code ${code}`);
subprocess = null;
subprocess.on("close", (code) => {
console.log(`child process exited with code ${code}`);
subprocess = null;
});
}
});
})
.then(() => tcpPortUsed.waitUntilUsed(PORT, 300, 30000))
Expand Down
2 changes: 1 addition & 1 deletion plugins/inference-plugin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@janhq/inference-plugin",
"version": "1.0.13",
"version": "1.0.14",
"description": "Inference Plugin, powered by @janhq/nitro, bring a high-performance Llama model inference in pure C++.",
"icon": "https://raw.githubusercontent.com/tailwindlabs/heroicons/88e98b0c2b458553fbadccddc2d2f878edc0387b/src/20/solid/command-line.svg",
"main": "dist/index.js",
Expand Down
2 changes: 1 addition & 1 deletion web/app/_components/ConversationalList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const ConversationalList: React.FC<Props> = ({ models }) => (
</span>
</div>
<div className="scroll mt-2 flex w-full gap-2 overflow-hidden overflow-x-scroll pl-6">
{models.map((item) => (
{models?.map((item) => (
<ConversationalCard key={item._id} model={item} />
))}
</div>
Expand Down
3 changes: 1 addition & 2 deletions web/app/_components/CreateBotContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ import DraggableProgressBar from '../DraggableProgressBar'
import { useSetAtom } from 'jotai'
import { activeBotAtom } from '@helpers/atoms/Bot.atom'
import {
leftSideBarExpandStateAtom,
rightSideBarExpandStateAtom,
} from '@helpers/atoms/SideBarExpand.atom'
import {
MainViewState,
setMainViewStateAtom,
} from '@helpers/atoms/MainView.atom'
import { executeSerial } from '../../../../electron/core/plugin-manager/execution/extension-manager'
import { DataService } from '@janhq/core'
import { executeSerial } from '@services/pluginService'

const CreateBotContainer: React.FC = () => {
const { downloadedModels } = useGetDownloadedModels()
Expand Down
2 changes: 1 addition & 1 deletion web/app/_components/HistoryItem/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react'
import { useAtomValue, useSetAtom } from 'jotai'
import { ModelManagementService } from '@janhq/core'
import { executeSerial } from '../../../../electron/core/plugin-manager/execution/extension-manager'
import {
getActiveConvoIdAtom,
setActiveConvoIdAtom,
Expand All @@ -13,6 +12,7 @@ import {
} from '@helpers/atoms/MainView.atom'
import { displayDate } from '@utils/datetime'
import { twMerge } from 'tailwind-merge'
import { executeSerial } from '@services/pluginService'

type Props = {
conversation: Conversation
Expand Down
2 changes: 1 addition & 1 deletion web/app/_components/LeftRibbonNav/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const LeftRibbonNav: React.FC = () => {

const onBotListClick = async () => {
const bots = await getAllBots()
if (bots.length === 0) {
if (bots?.length === 0) {
alert('You have no bot')
return
}
Expand Down
2 changes: 1 addition & 1 deletion web/app/_components/ModelTable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const ModelTable: React.FC<Props> = ({ models }) => (
</tr>
</thead>
<tbody>
{models.map((model) => (
{models?.map((model) => (
<ModelRow key={model._id} model={model} />
))}
</tbody>
Expand Down
4 changes: 3 additions & 1 deletion web/containers/BottomBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { useAtomValue } from 'jotai'
import { modelDownloadStateAtom } from '@helpers/atoms/DownloadState.atom'
import { formatDownloadPercentage } from '@utils/converter'
import { activeAssistantModelAtom, stateModel } from '@helpers/atoms/Model.atom'
import useGetAppVersion from '@hooks/useGetAppVersion'

const BottomBar = () => {
const activeModel = useAtomValue(activeAssistantModelAtom)
const stateModelStartStop = useAtomValue(stateModel)
const { ram, cpu } = useGetSystemResources()
const modelDownloadStates = useAtomValue(modelDownloadStateAtom)
const appVersion = useGetAppVersion()

const downloadStates: DownloadState[] = []
for (const [, value] of Object.entries(modelDownloadStates)) {
Expand Down Expand Up @@ -51,7 +53,7 @@ const BottomBar = () => {
<div className="flex gap-x-2">
<SystemItem name="CPU:" value={`${cpu}%`} />
<SystemItem name="Mem:" value={`${ram}%`} />
<p className="text-xs font-semibold">Jan v0.2.0</p>
<p className="text-xs font-semibold">Jan {appVersion?.version ?? ''}</p>
</div>
</div>
)
Expand Down
6 changes: 1 addition & 5 deletions web/containers/Providers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,9 @@ import {
activationPoints,
extensionPoints,
} from '../../../electron/core/plugin-manager/execution/index'
import {
isCorePluginInstalled,
setupBasePlugins,
} from '@services/pluginService'
import EventListenerWrapper from '@helpers/EventListenerWrapper'
import { setupCoreServices } from '@services/coreService'
import { executeSerial } from '../../../electron/core/plugin-manager/execution/extension-manager'
import { executeSerial, isCorePluginInstalled, setupBasePlugins } from '@services/pluginService'

const Providers = (props: PropsWithChildren) => {
const [setupCore, setSetupCore] = useState(false)
Expand Down
4 changes: 2 additions & 2 deletions web/containers/Sidebar/Left.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export const SidebarLeft = () => {

const onBotListClick = async () => {
const bots = await getAllBots()
if (bots.length === 0) {
alert('You have no bot')
if (!bots || bots?.length === 0) {
alert('You have not created any bot')
return
}

Expand Down
2 changes: 1 addition & 1 deletion web/helpers/EventListenerWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import { useSetAtom } from 'jotai'
import { ReactNode, useEffect } from 'react'
import { appDownloadProgress } from './JotaiWrapper'
import { executeSerial } from '../../electron/core/plugin-manager/execution/extension-manager'
import { ModelManagementService } from '@janhq/core'
import {
setDownloadStateAtom,
Expand All @@ -12,6 +11,7 @@ import {
import { getDownloadedModels } from '../hooks/useGetDownloadedModels'
import { downloadedModelAtom } from './atoms/DownloadedModel.atom'
import EventHandler from './EventHandler'
import { executeSerial } from '@services/pluginService'

type Props = {
children: ReactNode
Expand Down
5 changes: 3 additions & 2 deletions web/hooks/useCreateBot.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { executeSerial } from '../../electron/core/plugin-manager/execution/extension-manager'
import { DataService } from '@janhq/core'
import { executeSerial } from '@services/pluginService'

export default function useCreateBot() {
const createBot = async (bot: Bot) => {
try {
await executeSerial('createBot', bot)
await executeSerial(DataService.CreateBot, bot)
} catch (err) {
alert(err)
console.error(err)
Expand Down
5 changes: 3 additions & 2 deletions web/hooks/useDeleteBot.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { useSetAtom } from 'jotai'
import { executeSerial } from '../../electron/core/plugin-manager/execution/extension-manager'
import { activeBotAtom } from '@helpers/atoms/Bot.atom'
import { rightSideBarExpandStateAtom } from '@helpers/atoms/SideBarExpand.atom'
import { executeSerial } from '@services/pluginService'
import { DataService } from '@janhq/core'

export default function useDeleteBot() {
const setActiveBot = useSetAtom(activeBotAtom)
const setRightPanelVisibility = useSetAtom(rightSideBarExpandStateAtom)

const deleteBot = async (botId: string): Promise<'success' | 'failed'> => {
try {
await executeSerial('deleteBot', botId)
await executeSerial(DataService.DeleteBot, botId)
setRightPanelVisibility(false)
setActiveBot(undefined)
return 'success'
Expand Down
2 changes: 1 addition & 1 deletion web/hooks/useGetAppVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default function useGetAppVersion() {
}, [])

const getAppVersion = () => {
window.electronAPI.appVersion().then((version: string | undefined) => {
window.coreAPI?.appVersion().then((version: string | undefined) => {
setVersion(version ?? '')
})
}
Expand Down
7 changes: 4 additions & 3 deletions web/hooks/useGetBots.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { executeSerial } from '../../electron/core/plugin-manager/execution/extension-manager'
import { DataService } from '@janhq/core'
import { executeSerial } from '@services/pluginService'

export default function useGetBots() {
const getAllBots = async (): Promise<Bot[]> => {
try {
const bots = await executeSerial('getBots')
const bots = await executeSerial(DataService.GetBots)
return bots
} catch (err) {
alert(`Failed to get bots: ${err}`)
Expand All @@ -14,7 +15,7 @@ export default function useGetBots() {

const getBotById = async (botId: string): Promise<Bot | undefined> => {
try {
const bot: Bot = await executeSerial('getBotById', botId)
const bot: Bot = await executeSerial(DataService.GetBotById, botId)
return bot
} catch (err) {
alert(`Failed to get bot ${botId}: ${err}`)
Expand Down
6 changes: 5 additions & 1 deletion web/hooks/useGetDownloadedModels.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useEffect } from 'react'
import { executeSerial } from '../../electron/core/plugin-manager/execution/extension-manager'
import { ModelManagementService } from '@janhq/core'
import { useAtom } from 'jotai'
import { downloadedModelAtom } from '@helpers/atoms/DownloadedModel.atom'
import { extensionPoints } from '../../electron/core/plugin-manager/execution'
import { executeSerial } from '@services/pluginService'

export function useGetDownloadedModels() {
const [downloadedModels, setDownloadedModels] = useAtom(downloadedModelAtom)
Expand All @@ -17,6 +18,9 @@ export function useGetDownloadedModels() {
}

export async function getDownloadedModels(): Promise<AssistantModel[]> {
if (!extensionPoints.get(ModelManagementService.GetFinishedDownloadModels)) {
return []
}
const downloadedModels: AssistantModel[] = await executeSerial(
ModelManagementService.GetFinishedDownloadModels
)
Expand Down
2 changes: 1 addition & 1 deletion web/hooks/useGetSystemResources.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useEffect, useState } from 'react'
import { executeSerial } from '../../electron/core/plugin-manager/execution/extension-manager'
import { extensionPoints } from '../../electron/core/plugin-manager/execution'
import { SystemMonitoringService } from '@janhq/core'
import { useSetAtom } from 'jotai'
import { totalRamAtom } from '@helpers/atoms/SystemBar.atom'
import { executeSerial } from '@services/pluginService'
export default function useGetSystemResources() {
const [ram, setRam] = useState<number>(0)
const [cpu, setCPU] = useState<number>(0)
Expand Down
Loading

0 comments on commit 4c1ac12

Please sign in to comment.