mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-03-28 19:49:25 +01:00
Compare commits
4 Commits
main
...
v1.29.0-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6a32a548c | ||
|
|
5a35856747 | ||
|
|
6783cda222 | ||
|
|
175d63da8b |
|
|
@ -22,6 +22,7 @@ export default class OllamaController {
|
|||
recommendedOnly: reqData.recommendedOnly,
|
||||
query: reqData.query || null,
|
||||
limit: reqData.limit || 15,
|
||||
force: reqData.force,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ export class OllamaService {
|
|||
}
|
||||
|
||||
async getAvailableModels(
|
||||
{ sort, recommendedOnly, query, limit }: { sort?: 'pulls' | 'name'; recommendedOnly?: boolean, query: string | null, limit?: number } = {
|
||||
{ sort, recommendedOnly, query, limit, force }: { sort?: 'pulls' | 'name'; recommendedOnly?: boolean, query: string | null, limit?: number, force?: boolean } = {
|
||||
sort: 'pulls',
|
||||
recommendedOnly: false,
|
||||
query: null,
|
||||
|
|
@ -191,7 +191,7 @@ export class OllamaService {
|
|||
}
|
||||
): Promise<{ models: NomadOllamaModel[], hasMore: boolean } | null> {
|
||||
try {
|
||||
const models = await this.retrieveAndRefreshModels(sort)
|
||||
const models = await this.retrieveAndRefreshModels(sort, force)
|
||||
if (!models) {
|
||||
// If we fail to get models from the API, return the fallback recommended models
|
||||
logger.warn(
|
||||
|
|
@ -244,13 +244,18 @@ export class OllamaService {
|
|||
}
|
||||
|
||||
private async retrieveAndRefreshModels(
|
||||
sort?: 'pulls' | 'name'
|
||||
sort?: 'pulls' | 'name',
|
||||
force?: boolean
|
||||
): Promise<NomadOllamaModel[] | null> {
|
||||
try {
|
||||
const cachedModels = await this.readModelsFromCache()
|
||||
if (cachedModels) {
|
||||
logger.info('[OllamaService] Using cached available models data')
|
||||
return this.sortModels(cachedModels, sort)
|
||||
if (!force) {
|
||||
const cachedModels = await this.readModelsFromCache()
|
||||
if (cachedModels) {
|
||||
logger.info('[OllamaService] Using cached available models data')
|
||||
return this.sortModels(cachedModels, sort)
|
||||
}
|
||||
} else {
|
||||
logger.info('[OllamaService] Force refresh requested, bypassing cache')
|
||||
}
|
||||
|
||||
logger.info('[OllamaService] Fetching fresh available models from API')
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { DockerService } from '#services/docker_service'
|
|||
import { ServiceSlim } from '../../types/services.js'
|
||||
import logger from '@adonisjs/core/services/logger'
|
||||
import si from 'systeminformation'
|
||||
import { NomadDiskInfo, NomadDiskInfoRaw, SystemInformationResponse } from '../../types/system.js'
|
||||
import { GpuHealthStatus, NomadDiskInfo, NomadDiskInfoRaw, SystemInformationResponse } from '../../types/system.js'
|
||||
import { SERVICE_NAMES } from '../../constants/service_names.js'
|
||||
import { readFileSync } from 'fs'
|
||||
import path, { join } from 'path'
|
||||
|
|
@ -235,6 +235,13 @@ export class SystemService {
|
|||
logger.error('Error reading disk info file:', error)
|
||||
}
|
||||
|
||||
// GPU health tracking — detect when host has NVIDIA GPU but Ollama can't access it
|
||||
let gpuHealth: GpuHealthStatus = {
|
||||
status: 'no_gpu',
|
||||
hasNvidiaRuntime: false,
|
||||
ollamaGpuAccessible: false,
|
||||
}
|
||||
|
||||
// Query Docker API for host-level info (hostname, OS, GPU runtime)
|
||||
// si.osInfo() returns the container's info inside Docker, not the host's
|
||||
try {
|
||||
|
|
@ -255,6 +262,7 @@ export class SystemService {
|
|||
if (!graphics.controllers || graphics.controllers.length === 0) {
|
||||
const runtimes = dockerInfo.Runtimes || {}
|
||||
if ('nvidia' in runtimes) {
|
||||
gpuHealth.hasNvidiaRuntime = true
|
||||
const nvidiaInfo = await this.getNvidiaSmiInfo()
|
||||
if (Array.isArray(nvidiaInfo)) {
|
||||
graphics.controllers = nvidiaInfo.map((gpu) => ({
|
||||
|
|
@ -264,10 +272,19 @@ export class SystemService {
|
|||
vram: gpu.vram,
|
||||
vramDynamic: false, // assume false here, we don't actually use this field for our purposes.
|
||||
}))
|
||||
gpuHealth.status = 'ok'
|
||||
gpuHealth.ollamaGpuAccessible = true
|
||||
} else if (nvidiaInfo === 'OLLAMA_NOT_FOUND') {
|
||||
gpuHealth.status = 'ollama_not_installed'
|
||||
} else {
|
||||
logger.warn(`NVIDIA runtime detected but failed to get GPU info: ${typeof nvidiaInfo === 'string' ? nvidiaInfo : JSON.stringify(nvidiaInfo)}`)
|
||||
gpuHealth.status = 'passthrough_failed'
|
||||
logger.warn(`NVIDIA runtime detected but GPU passthrough failed: ${typeof nvidiaInfo === 'string' ? nvidiaInfo : JSON.stringify(nvidiaInfo)}`)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// si.graphics() returned controllers (host install, not Docker) — GPU is working
|
||||
gpuHealth.status = 'ok'
|
||||
gpuHealth.ollamaGpuAccessible = true
|
||||
}
|
||||
} catch {
|
||||
// Docker info query failed, skip host-level enrichment
|
||||
|
|
@ -282,6 +299,7 @@ export class SystemService {
|
|||
fsSize,
|
||||
uptime,
|
||||
graphics,
|
||||
gpuHealth,
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error getting system info:', error)
|
||||
|
|
|
|||
|
|
@ -19,5 +19,6 @@ export const getAvailableModelsSchema = vine.compile(
|
|||
recommendedOnly: vine.boolean().optional(),
|
||||
query: vine.string().trim().optional(),
|
||||
limit: vine.number().positive().optional(),
|
||||
force: vine.boolean().optional(),
|
||||
})
|
||||
)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ const StyledButton: React.FC<StyledButtonProps> = ({
|
|||
size = 'md',
|
||||
loading = false,
|
||||
fullWidth = false,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
const isDisabled = useMemo(() => {
|
||||
|
|
@ -152,7 +153,8 @@ const StyledButton: React.FC<StyledButtonProps> = ({
|
|||
getSizeClasses(),
|
||||
getVariantClasses(),
|
||||
isDisabled ? 'pointer-events-none opacity-60' : 'cursor-pointer',
|
||||
'items-center justify-center rounded-md font-semibold focus:outline-none focus:ring-2 focus:ring-desert-green-light focus:ring-offset-2 focus:ring-offset-desert-sand disabled:cursor-not-allowed disabled:shadow-none'
|
||||
'items-center justify-center rounded-md font-semibold focus:outline-none focus:ring-2 focus:ring-desert-green-light focus:ring-offset-2 focus:ring-offset-desert-sand disabled:cursor-not-allowed disabled:shadow-none',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
disabled={isDisabled}
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ class API {
|
|||
})()
|
||||
}
|
||||
|
||||
async getAvailableModels(params: { query?: string; recommendedOnly?: boolean; limit?: number }) {
|
||||
async getAvailableModels(params: { query?: string; recommendedOnly?: boolean; limit?: number; force?: boolean }) {
|
||||
return catchInternal(async () => {
|
||||
const response = await this.client.get<{
|
||||
models: NomadOllamaModel[]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Head, router, usePage } from '@inertiajs/react'
|
||||
import { useState } from 'react'
|
||||
import { useRef, useState } from 'react'
|
||||
import StyledTable from '~/components/StyledTable'
|
||||
import SettingsLayout from '~/layouts/SettingsLayout'
|
||||
import { NomadOllamaModel } from '../../../types/ollama'
|
||||
|
|
@ -16,9 +16,10 @@ import Switch from '~/components/inputs/Switch'
|
|||
import StyledSectionHeader from '~/components/StyledSectionHeader'
|
||||
import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import Input from '~/components/inputs/Input'
|
||||
import { IconSearch } from '@tabler/icons-react'
|
||||
import { IconSearch, IconRefresh } from '@tabler/icons-react'
|
||||
import useDebounce from '~/hooks/useDebounce'
|
||||
import ActiveModelDownloads from '~/components/ActiveModelDownloads'
|
||||
import { useSystemInfo } from '~/hooks/useSystemInfo'
|
||||
|
||||
export default function ModelsPage(props: {
|
||||
models: {
|
||||
|
|
@ -32,6 +33,64 @@ export default function ModelsPage(props: {
|
|||
const { addNotification } = useNotifications()
|
||||
const { openModal, closeAllModals } = useModals()
|
||||
const { debounce } = useDebounce()
|
||||
const { data: systemInfo } = useSystemInfo({})
|
||||
|
||||
const [gpuBannerDismissed, setGpuBannerDismissed] = useState(() => {
|
||||
try {
|
||||
return localStorage.getItem('nomad:gpu-banner-dismissed') === 'true'
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
const [reinstalling, setReinstalling] = useState(false)
|
||||
|
||||
const handleDismissGpuBanner = () => {
|
||||
setGpuBannerDismissed(true)
|
||||
try {
|
||||
localStorage.setItem('nomad:gpu-banner-dismissed', 'true')
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const handleForceReinstallOllama = () => {
|
||||
openModal(
|
||||
<StyledModal
|
||||
title="Reinstall AI Assistant?"
|
||||
onConfirm={async () => {
|
||||
closeAllModals()
|
||||
setReinstalling(true)
|
||||
try {
|
||||
const response = await api.forceReinstallService('nomad_ollama')
|
||||
if (!response || !response.success) {
|
||||
throw new Error(response?.message || 'Force reinstall failed')
|
||||
}
|
||||
addNotification({
|
||||
message: `${aiAssistantName} is being reinstalled with GPU support. This page will reload shortly.`,
|
||||
type: 'success',
|
||||
})
|
||||
try { localStorage.removeItem('nomad:gpu-banner-dismissed') } catch {}
|
||||
setTimeout(() => window.location.reload(), 5000)
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: `Failed to reinstall: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
type: 'error',
|
||||
})
|
||||
setReinstalling(false)
|
||||
}
|
||||
}}
|
||||
onCancel={closeAllModals}
|
||||
open={true}
|
||||
confirmText="Reinstall"
|
||||
cancelText="Cancel"
|
||||
>
|
||||
<p className="text-gray-700">
|
||||
This will recreate the {aiAssistantName} container with GPU support enabled.
|
||||
Your downloaded models will be preserved. The service will be briefly
|
||||
unavailable during reinstall.
|
||||
</p>
|
||||
</StyledModal>,
|
||||
'gpu-health-force-reinstall-modal'
|
||||
)
|
||||
}
|
||||
const [chatSuggestionsEnabled, setChatSuggestionsEnabled] = useState(
|
||||
props.models.settings.chatSuggestionsEnabled
|
||||
)
|
||||
|
|
@ -47,13 +106,19 @@ export default function ModelsPage(props: {
|
|||
setQuery(val)
|
||||
}, 300)
|
||||
|
||||
const { data: availableModelData, isFetching } = useQuery({
|
||||
const forceRefreshRef = useRef(false)
|
||||
const [isForceRefreshing, setIsForceRefreshing] = useState(false)
|
||||
|
||||
const { data: availableModelData, isFetching, refetch } = useQuery({
|
||||
queryKey: ['ollama', 'availableModels', query, limit],
|
||||
queryFn: async () => {
|
||||
const force = forceRefreshRef.current
|
||||
forceRefreshRef.current = false
|
||||
const res = await api.getAvailableModels({
|
||||
query,
|
||||
recommendedOnly: false,
|
||||
limit,
|
||||
force: force || undefined,
|
||||
})
|
||||
if (!res) {
|
||||
return {
|
||||
|
|
@ -66,6 +131,14 @@ export default function ModelsPage(props: {
|
|||
initialData: { models: props.models.availableModels, hasMore: false },
|
||||
})
|
||||
|
||||
async function handleForceRefresh() {
|
||||
forceRefreshRef.current = true
|
||||
setIsForceRefreshing(true)
|
||||
await refetch()
|
||||
setIsForceRefreshing(false)
|
||||
addNotification({ message: 'Model list refreshed from remote.', type: 'success' })
|
||||
}
|
||||
|
||||
async function handleInstallModel(modelName: string) {
|
||||
try {
|
||||
const res = await api.downloadModel(modelName)
|
||||
|
|
@ -164,6 +237,26 @@ export default function ModelsPage(props: {
|
|||
className="!mt-6"
|
||||
/>
|
||||
)}
|
||||
{isInstalled && systemInfo?.gpuHealth?.status === 'passthrough_failed' && !gpuBannerDismissed && (
|
||||
<Alert
|
||||
type="warning"
|
||||
variant="bordered"
|
||||
title="GPU Not Accessible"
|
||||
message={`Your system has an NVIDIA GPU, but ${aiAssistantName} can't access it. AI is running on CPU only, which is significantly slower.`}
|
||||
className="!mt-6"
|
||||
dismissible={true}
|
||||
onDismiss={handleDismissGpuBanner}
|
||||
buttonProps={{
|
||||
children: `Fix: Reinstall ${aiAssistantName}`,
|
||||
icon: 'IconRefresh',
|
||||
variant: 'action',
|
||||
size: 'sm',
|
||||
onClick: handleForceReinstallOllama,
|
||||
loading: reinstalling,
|
||||
disabled: reinstalling,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<StyledSectionHeader title="Settings" className="mt-8 mb-4" />
|
||||
<div className="bg-white rounded-lg border-2 border-gray-200 p-6">
|
||||
|
|
@ -196,7 +289,7 @@ export default function ModelsPage(props: {
|
|||
<ActiveModelDownloads withHeader />
|
||||
|
||||
<StyledSectionHeader title="Models" className="mt-12 mb-4" />
|
||||
<div className="flex justify-start mt-4">
|
||||
<div className="flex justify-start items-center gap-3 mt-4">
|
||||
<Input
|
||||
name="search"
|
||||
label=""
|
||||
|
|
@ -209,6 +302,15 @@ export default function ModelsPage(props: {
|
|||
className="w-1/3"
|
||||
leftIcon={<IconSearch className="w-5 h-5 text-gray-400" />}
|
||||
/>
|
||||
<StyledButton
|
||||
variant="secondary"
|
||||
onClick={handleForceRefresh}
|
||||
icon="IconRefresh"
|
||||
loading={isForceRefreshing}
|
||||
className='mt-1'
|
||||
>
|
||||
Refresh Models
|
||||
</StyledButton>
|
||||
</div>
|
||||
<StyledTable<NomadOllamaModel>
|
||||
className="font-semibold mt-4"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { useState } from 'react'
|
||||
import { Head } from '@inertiajs/react'
|
||||
import SettingsLayout from '~/layouts/SettingsLayout'
|
||||
import { SystemInformationResponse } from '../../../types/system'
|
||||
|
|
@ -6,7 +7,11 @@ import CircularGauge from '~/components/systeminfo/CircularGauge'
|
|||
import HorizontalBarChart from '~/components/HorizontalBarChart'
|
||||
import InfoCard from '~/components/systeminfo/InfoCard'
|
||||
import Alert from '~/components/Alert'
|
||||
import StyledModal from '~/components/StyledModal'
|
||||
import { useSystemInfo } from '~/hooks/useSystemInfo'
|
||||
import { useNotifications } from '~/context/NotificationContext'
|
||||
import { useModals } from '~/context/ModalContext'
|
||||
import api from '~/lib/api'
|
||||
import StatusCard from '~/components/systeminfo/StatusCard'
|
||||
import { IconCpu, IconDatabase, IconServer, IconDeviceDesktop, IconComponents } from '@tabler/icons-react'
|
||||
|
||||
|
|
@ -16,6 +21,65 @@ export default function SettingsPage(props: {
|
|||
const { data: info } = useSystemInfo({
|
||||
initialData: props.system.info,
|
||||
})
|
||||
const { addNotification } = useNotifications()
|
||||
const { openModal, closeAllModals } = useModals()
|
||||
|
||||
const [gpuBannerDismissed, setGpuBannerDismissed] = useState(() => {
|
||||
try {
|
||||
return localStorage.getItem('nomad:gpu-banner-dismissed') === 'true'
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
const [reinstalling, setReinstalling] = useState(false)
|
||||
|
||||
const handleDismissGpuBanner = () => {
|
||||
setGpuBannerDismissed(true)
|
||||
try {
|
||||
localStorage.setItem('nomad:gpu-banner-dismissed', 'true')
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const handleForceReinstallOllama = () => {
|
||||
openModal(
|
||||
<StyledModal
|
||||
title="Reinstall AI Assistant?"
|
||||
onConfirm={async () => {
|
||||
closeAllModals()
|
||||
setReinstalling(true)
|
||||
try {
|
||||
const response = await api.forceReinstallService('nomad_ollama')
|
||||
if (!response || !response.success) {
|
||||
throw new Error(response?.message || 'Force reinstall failed')
|
||||
}
|
||||
addNotification({
|
||||
message: 'AI Assistant is being reinstalled with GPU support. This page will reload shortly.',
|
||||
type: 'success',
|
||||
})
|
||||
try { localStorage.removeItem('nomad:gpu-banner-dismissed') } catch {}
|
||||
setTimeout(() => window.location.reload(), 5000)
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: `Failed to reinstall: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
type: 'error',
|
||||
})
|
||||
setReinstalling(false)
|
||||
}
|
||||
}}
|
||||
onCancel={closeAllModals}
|
||||
open={true}
|
||||
confirmText="Reinstall"
|
||||
cancelText="Cancel"
|
||||
>
|
||||
<p className="text-gray-700">
|
||||
This will recreate the AI Assistant container with GPU support enabled.
|
||||
Your downloaded models will be preserved. The service will be briefly
|
||||
unavailable during reinstall.
|
||||
</p>
|
||||
</StyledModal>,
|
||||
'gpu-health-force-reinstall-modal'
|
||||
)
|
||||
}
|
||||
|
||||
// Use (total - available) to reflect actual memory pressure.
|
||||
// mem.used includes reclaimable buff/cache on Linux, which inflates the number.
|
||||
|
|
@ -173,6 +237,27 @@ export default function SettingsPage(props: {
|
|||
},
|
||||
]}
|
||||
/>
|
||||
{info?.gpuHealth?.status === 'passthrough_failed' && !gpuBannerDismissed && (
|
||||
<div className="lg:col-span-2">
|
||||
<Alert
|
||||
type="warning"
|
||||
variant="bordered"
|
||||
title="GPU Not Accessible to AI Assistant"
|
||||
message="Your system has an NVIDIA GPU, but the AI Assistant can't access it. AI is running on CPU only, which is significantly slower."
|
||||
dismissible={true}
|
||||
onDismiss={handleDismissGpuBanner}
|
||||
buttonProps={{
|
||||
children: 'Fix: Reinstall AI Assistant',
|
||||
icon: 'IconRefresh',
|
||||
variant: 'action',
|
||||
size: 'sm',
|
||||
onClick: handleForceReinstallOllama,
|
||||
loading: reinstalling,
|
||||
disabled: reinstalling,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{info?.graphics?.controllers && info.graphics.controllers.length > 0 && (
|
||||
<InfoCard
|
||||
title="Graphics"
|
||||
|
|
|
|||
18
admin/package-lock.json
generated
18
admin/package-lock.json
generated
|
|
@ -66,7 +66,7 @@
|
|||
"stopword": "^3.1.5",
|
||||
"systeminformation": "^5.30.8",
|
||||
"tailwindcss": "^4.1.10",
|
||||
"tar": "^7.5.9",
|
||||
"tar": "^7.5.10",
|
||||
"tesseract.js": "^7.0.0",
|
||||
"url-join": "^5.0.0",
|
||||
"yaml": "^2.8.0"
|
||||
|
|
@ -4379,7 +4379,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -4396,7 +4395,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -4413,7 +4411,6 @@
|
|||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -4430,7 +4427,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -4447,7 +4443,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -4464,7 +4459,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -4481,7 +4475,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -4498,7 +4491,6 @@
|
|||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -4515,7 +4507,6 @@
|
|||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -4532,7 +4523,6 @@
|
|||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
|
@ -15275,9 +15265,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.9.tgz",
|
||||
"integrity": "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==",
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.10.tgz",
|
||||
"integrity": "sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/fs-minipass": "^4.0.0",
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@
|
|||
"stopword": "^3.1.5",
|
||||
"systeminformation": "^5.30.8",
|
||||
"tailwindcss": "^4.1.10",
|
||||
"tar": "^7.5.9",
|
||||
"tar": "^7.5.10",
|
||||
"tesseract.js": "^7.0.0",
|
||||
"url-join": "^5.0.0",
|
||||
"yaml": "^2.8.0"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import { Systeminformation } from 'systeminformation'
|
||||
|
||||
export type GpuHealthStatus = {
|
||||
status: 'ok' | 'passthrough_failed' | 'no_gpu' | 'ollama_not_installed'
|
||||
hasNvidiaRuntime: boolean
|
||||
ollamaGpuAccessible: boolean
|
||||
}
|
||||
|
||||
export type SystemInformationResponse = {
|
||||
cpu: Systeminformation.CpuData
|
||||
mem: Systeminformation.MemData
|
||||
|
|
@ -9,6 +15,7 @@ export type SystemInformationResponse = {
|
|||
fsSize: Systeminformation.FsSizeData[]
|
||||
uptime: Systeminformation.TimeData
|
||||
graphics: Systeminformation.GraphicsData
|
||||
gpuHealth?: GpuHealthStatus
|
||||
}
|
||||
|
||||
// Type inferrence is not working properly with usePage and shared props, so we define this type manually
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "project-nomad",
|
||||
"version": "1.28.0",
|
||||
"version": "1.29.0-rc.1",
|
||||
"description": "\"",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user