Skip to content
Merged
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
21 changes: 21 additions & 0 deletions openviking/server/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,27 @@ def main():
print(e, file=sys.stderr)
sys.exit(1)

# Ensure Ollama is running if configured
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

考虑将环境检测,封装成一个独立的模块(可以在下次提交),非 blocking

try:
from openviking_cli.utils.ollama import detect_ollama_in_config, ensure_ollama_for_server

ov_config = OpenVikingConfigSingleton.get_instance()
uses_ollama, ollama_host, ollama_port = detect_ollama_in_config(ov_config)
if uses_ollama:
result = ensure_ollama_for_server(ollama_host, ollama_port)
if result.success:
print(f"Ollama is running at {ollama_host}:{ollama_port}")
else:
print(
f"Warning: Ollama not available at {ollama_host}:{ollama_port}. "
f"Embedding/VLM may fail. ({result.message})",
file=sys.stderr,
)
if result.stderr_output:
print(f" Ollama stderr: {result.stderr_output}", file=sys.stderr)
except Exception as e:
print(f"Warning: Ollama pre-flight check failed: {e}", file=sys.stderr)

# Override with command line arguments
if args.host is not None:
config.host = _normalize_host_arg(args.host)
Expand Down
17 changes: 17 additions & 0 deletions openviking/server/routers/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,23 @@ async def readiness_check(request: Request):
except Exception as e:
checks["api_key_manager"] = f"error: {e}"

# 4. Ollama: connectivity check if configured
try:
from openviking_cli.utils.config.open_viking_config import OpenVikingConfigSingleton
from openviking_cli.utils.ollama import check_ollama_running, detect_ollama_in_config

ov_config = OpenVikingConfigSingleton.get_instance()
uses_ollama, ollama_host, ollama_port = detect_ollama_in_config(ov_config)
if uses_ollama:
if check_ollama_running(ollama_host, ollama_port):
checks["ollama"] = "ok"
else:
checks["ollama"] = f"unreachable at {ollama_host}:{ollama_port}"
else:
checks["ollama"] = "not_configured"
except Exception as e:
checks["ollama"] = f"error: {e}"

all_ok = all(v in ("ok", "not_configured") for v in checks.values())
status_code = 200 if all_ok else 503
return JSONResponse(
Expand Down
52 changes: 49 additions & 3 deletions openviking_cli/doctor.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Copyright (c) 2026 Beijing Volcano Engine Technology Co., Ltd.
# SPDX-License-Identifier: AGPL-3.0
"""ov doctor - validate OpenViking subsystems and report actionable diagnostics.
"""openviking-server doctor - validate OpenViking subsystems and report actionable diagnostics.

Unlike ``ov health`` (which pings a running server), ``ov doctor`` checks
Unlike ``ov health`` (which pings a running server), ``openviking-server doctor`` checks
local prerequisites without requiring a server: config file, Python version,
native vector engine, AGFS, embedding provider, VLM provider, and disk space.
"""
Expand Down Expand Up @@ -160,6 +160,10 @@ def check_embedding() -> tuple[bool, str, Optional[str]]:
if provider == "unknown":
return False, "No embedding provider configured", "Add embedding.dense section to ov.conf"

# Ollama doesn't need an API key
if provider == "ollama":
return True, f"{provider}/{model}", None

api_key = dense.get("api_key", "")
if not api_key or api_key.startswith("{"):
return (
Expand Down Expand Up @@ -188,6 +192,10 @@ def check_vlm() -> tuple[bool, str, Optional[str]]:
if not provider:
return False, "No VLM provider configured", "Add vlm section to ov.conf"

# Ollama via LiteLLM doesn't need a real API key
if provider == "litellm" and model.startswith("ollama/"):
return True, f"{provider}/{model}", None
Comment thread
MaojiaSheng marked this conversation as resolved.

api_key = vlm.get("api_key", "")
if not api_key or api_key.startswith("{"):
return (
Expand All @@ -199,6 +207,43 @@ def check_vlm() -> tuple[bool, str, Optional[str]]:
return True, f"{provider}/{model}", None


def check_ollama() -> tuple[bool, str, Optional[str]]:
"""Check Ollama connectivity if the config uses an Ollama provider."""
config_path = _find_config()
if config_path is None:
return True, "not configured", None

data = _load_config_json(config_path)
if data is None:
return True, "not configured", None

# Detect whether config uses Ollama
dense = data.get("embedding", {}).get("dense", {})
vlm = data.get("vlm", {})
uses_embedding = dense.get("provider") == "ollama"
uses_vlm = vlm.get("provider") == "litellm" and (vlm.get("model", "")).startswith("ollama/")

if not uses_embedding and not uses_vlm:
return True, "not configured", None

from openviking_cli.utils.ollama import check_ollama_running, parse_ollama_url

# Determine host/port from config
if uses_embedding:
host, port = parse_ollama_url(dense.get("api_base"))
else:
host, port = parse_ollama_url(vlm.get("api_base"))

if check_ollama_running(host, port):
return True, f"running at {host}:{port}", None

return (
False,
f"unreachable at {host}:{port}",
"Run 'ollama serve' or check your Ollama configuration",
)


def check_disk() -> tuple[bool, str, Optional[str]]:
"""Check free disk space in the workspace directory."""
config_path = _find_config()
Expand Down Expand Up @@ -237,6 +282,7 @@ def check_disk() -> tuple[bool, str, Optional[str]]:
("AGFS", check_agfs),
("Embedding", check_embedding),
("VLM", check_vlm),
("Ollama", check_ollama),
("Disk", check_disk),
]

Expand Down Expand Up @@ -279,5 +325,5 @@ def run_doctor() -> int:


def main() -> int:
"""Entry point for ``ov doctor``."""
"""Entry point for ``openviking-server doctor``."""
return run_doctor()
14 changes: 14 additions & 0 deletions openviking_cli/server_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,27 @@
The real bootstrap logic stays in ``openviking.server.bootstrap``; we just
pre-parse ``--config`` and set the environment variable before that module
is ever imported.

Subcommands ``init`` and ``doctor`` are handled here directly (they don't
need a running server).
"""

import os
import sys


def main():
# Intercept subcommands that don't need the server.
if len(sys.argv) > 1 and sys.argv[1] == "init":
from openviking_cli.setup_wizard import main as init_main

sys.exit(init_main())

if len(sys.argv) > 1 and sys.argv[1] == "doctor":
from openviking_cli.doctor import main as doctor_main

sys.exit(doctor_main())

# Pre-parse --config from sys.argv before any openviking imports,
# so the env var is visible when the config singleton first initialises.
for i, arg in enumerate(sys.argv):
Expand Down
Loading
Loading