Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 1 addition & 35 deletions openviking_cli/setup_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pathlib import Path
from typing import Any

from openviking_cli.utils.environment import get_system_ram_gb as _get_system_ram_gb
from openviking_cli.utils.config.consts import DEFAULT_CONFIG_DIR, OPENVIKING_CONFIG_ENV
from openviking_cli.utils.ollama import (
check_ollama_running,
Expand Down Expand Up @@ -224,41 +225,6 @@ def _configured_hint(enabled: bool) -> str:
# ---------------------------------------------------------------------------


def _get_system_ram_gb() -> int:
"""Get total system RAM in GB."""
try:
pages = os.sysconf("SC_PHYS_PAGES")
page_size = os.sysconf("SC_PAGE_SIZE")
return (pages * page_size) // (1024**3)
except (ValueError, OSError, AttributeError):
pass
# Windows fallback
try:
import ctypes

kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined]

class MEMORYSTATUSEX(ctypes.Structure):
_fields_ = [
("dwLength", ctypes.c_ulong),
("dwMemoryLoad", ctypes.c_ulong),
("ullTotalPhys", ctypes.c_ulonglong),
("ullAvailPhys", ctypes.c_ulonglong),
("ullTotalPageFile", ctypes.c_ulonglong),
("ullAvailPageFile", ctypes.c_ulonglong),
("ullTotalVirtual", ctypes.c_ulonglong),
("ullAvailVirtual", ctypes.c_ulonglong),
("ullAvailExtendedVirtual", ctypes.c_ulonglong),
]

stat = MEMORYSTATUSEX()
stat.dwLength = ctypes.sizeof(stat)
kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
return stat.ullTotalPhys // (1024**3)
except Exception:
return 0


# ---------------------------------------------------------------------------
# Ollama interaction (delegates to openviking_cli.utils.ollama)
# ---------------------------------------------------------------------------
Expand Down
43 changes: 43 additions & 0 deletions openviking_cli/utils/environment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd.
# SPDX-License-Identifier: AGPL-3.0
"""Environment detection helpers shared by OpenViking CLI entry points."""

from __future__ import annotations

import ctypes
import os


def get_system_ram_gb() -> int:
"""Return total physical RAM in GiB, or 0 when it cannot be detected."""
try:
pages = os.sysconf("SC_PHYS_PAGES")
page_size = os.sysconf("SC_PAGE_SIZE")
if pages > 0 and page_size > 0:
return int((pages * page_size) // (1024**3))
except (ValueError, OSError, AttributeError):
pass

try:
kernel32 = ctypes.windll.kernel32 # type: ignore[attr-defined]

class MEMORYSTATUSEX(ctypes.Structure):
_fields_ = [
("dwLength", ctypes.c_ulong),
("dwMemoryLoad", ctypes.c_ulong),
("ullTotalPhys", ctypes.c_ulonglong),
("ullAvailPhys", ctypes.c_ulonglong),
("ullTotalPageFile", ctypes.c_ulonglong),
("ullAvailPageFile", ctypes.c_ulonglong),
("ullTotalVirtual", ctypes.c_ulonglong),
("ullAvailVirtual", ctypes.c_ulonglong),
("ullAvailExtendedVirtual", ctypes.c_ulonglong),
]

stat = MEMORYSTATUSEX()
stat.dwLength = ctypes.sizeof(stat)
if kernel32.GlobalMemoryStatusEx(ctypes.byref(stat)):
return int(stat.ullTotalPhys // (1024**3))
except Exception:
pass
return 0
43 changes: 43 additions & 0 deletions tests/cli/test_environment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd.
# SPDX-License-Identifier: AGPL-3.0

from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import patch

from openviking_cli.utils import environment


def test_get_system_ram_gb_uses_sysconf_values():
def fake_sysconf(name: str) -> int:
values = {
"SC_PHYS_PAGES": 4 * 1024,
"SC_PAGE_SIZE": 1024 * 1024,
}
return values[name]

with patch.object(environment.os, "sysconf", side_effect=fake_sysconf):
assert environment.get_system_ram_gb() == 4


def test_get_system_ram_gb_uses_windows_fallback_when_sysconf_unavailable():
class FakeKernel32:
def GlobalMemoryStatusEx(self, stat_ptr):
stat_ptr._obj.ullTotalPhys = 16 * 1024**3
return 1

with patch.object(environment.os, "sysconf", side_effect=AttributeError):
with patch.object(
environment.ctypes,
"windll",
SimpleNamespace(kernel32=FakeKernel32()),
create=True,
):
assert environment.get_system_ram_gb() == 16


def test_get_system_ram_gb_returns_zero_when_detection_fails():
with patch.object(environment.os, "sysconf", side_effect=OSError):
with patch.object(environment.ctypes, "windll", None, create=True):
assert environment.get_system_ram_gb() == 0
Loading