✨ Add new files
This commit is contained in:
parent
24db364b44
commit
2d0ae9d5e4
19 changed files with 235 additions and 20 deletions
44
README.md
44
README.md
|
|
@ -17,10 +17,36 @@ Automated commit message generation service using local LLM inference. Commits a
|
||||||
cd /var/home/lilith/Code/@packages/@ml/auto-commit-service
|
cd /var/home/lilith/Code/@packages/@ml/auto-commit-service
|
||||||
pip install -e .
|
pip install -e .
|
||||||
|
|
||||||
|
# Or with model-boss integration (recommended)
|
||||||
|
pip install -e ".[model-boss]"
|
||||||
|
|
||||||
# Or with dev dependencies
|
# Or with dev dependencies
|
||||||
pip install -e ".[dev]"
|
pip install -e ".[dev]"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Model Configuration
|
||||||
|
|
||||||
|
The service requires a language model to generate commit messages. You have three options:
|
||||||
|
|
||||||
|
1. **Auto-load via model-boss (recommended)**:
|
||||||
|
```bash
|
||||||
|
pip install -e ".[model-boss]"
|
||||||
|
# Models will be auto-downloaded and cached
|
||||||
|
# Default: ministral-3b-instruct
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Manual model path**:
|
||||||
|
```bash
|
||||||
|
export LLAMA_SERVICE_FAST_MODEL_PATH=/path/to/model.gguf
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Disable auto-start** (use external llama-service):
|
||||||
|
```bash
|
||||||
|
export AUTO_COMMIT_LLAMA_SERVICE_AUTOSTART=false
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**: If no model is configured, the service will fail to start and report as down. This prevents making commits with placeholder messages.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Environment variables (prefix: `AUTO_COMMIT_`):
|
Environment variables (prefix: `AUTO_COMMIT_`):
|
||||||
|
|
@ -29,6 +55,9 @@ Environment variables (prefix: `AUTO_COMMIT_`):
|
||||||
|----------|---------|-------------|
|
|----------|---------|-------------|
|
||||||
| `AUTO_COMMIT_LLAMA_SERVICE_URL` | `http://localhost:8000` | llama-service URL |
|
| `AUTO_COMMIT_LLAMA_SERVICE_URL` | `http://localhost:8000` | llama-service URL |
|
||||||
| `AUTO_COMMIT_LLAMA_MODEL` | `fast` | Model to use (fast/reasoning) |
|
| `AUTO_COMMIT_LLAMA_MODEL` | `fast` | Model to use (fast/reasoning) |
|
||||||
|
| `AUTO_COMMIT_LLAMA_SERVICE_AUTOSTART` | `true` | Auto-start llama-service if down |
|
||||||
|
| `AUTO_COMMIT_LLAMA_FAST_MODEL_ID` | `ministral-3b-instruct` | Model ID for model-boss |
|
||||||
|
| `AUTO_COMMIT_USE_MODEL_BOSS` | `true` | Use model-boss for model loading |
|
||||||
| `AUTO_COMMIT_CYCLE_INTERVAL_SECONDS` | `900` | Seconds between cycles (15 min) |
|
| `AUTO_COMMIT_CYCLE_INTERVAL_SECONDS` | `900` | Seconds between cycles (15 min) |
|
||||||
| `AUTO_COMMIT_ENABLED` | `true` | Enable daemon on startup |
|
| `AUTO_COMMIT_ENABLED` | `true` | Enable daemon on startup |
|
||||||
| `AUTO_COMMIT_CLAUDE_FALLBACK_ENABLED` | `true` | Enable Claude Code recovery |
|
| `AUTO_COMMIT_CLAUDE_FALLBACK_ENABLED` | `true` | Enable Claude Code recovery |
|
||||||
|
|
@ -109,11 +138,13 @@ Generated commits follow the Lilith Platform convention:
|
||||||
|
|
||||||
## Error Handling
|
## Error Handling
|
||||||
|
|
||||||
1. **Push rejected**: Attempts `git pull --rebase` and retries
|
1. **No model configured**: Service fails to start, reported as down (prevents bad commits)
|
||||||
2. **Merge conflict**: Invokes Claude Code to resolve
|
2. **Push rejected**: Attempts `git pull --rebase` and retries
|
||||||
3. **Hook failure**: Invokes Claude Code to fix
|
3. **Merge conflict**: Invokes Claude Code to resolve
|
||||||
4. **LLM unavailable**: Skips cycle (logs warning)
|
4. **Hook failure**: Invokes Claude Code to fix
|
||||||
5. **Auth failure**: Skips repo (requires manual fix)
|
5. **LLM unavailable**: Service auto-starts if configured, otherwise skips cycle
|
||||||
|
6. **Infrastructure errors** (network, auth): Skips Claude recovery, reports error
|
||||||
|
7. **Auth failure**: Skips repo (requires manual fix)
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|
@ -152,10 +183,11 @@ auto_commit_service/
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- `tqftw-ml-service-base`: FastAPI patterns, lifespan, health checks
|
- `lilith-ml-service-base`: FastAPI patterns, lifespan, health checks
|
||||||
- `httpx`: Async HTTP client for llama-service
|
- `httpx`: Async HTTP client for llama-service
|
||||||
- `pydantic`: Configuration and models
|
- `pydantic`: Configuration and models
|
||||||
- `uvicorn`: ASGI server
|
- `uvicorn`: ASGI server
|
||||||
# Test update Mon Jan 5 12:27:47 PST 2026
|
# Test update Mon Jan 5 12:27:47 PST 2026
|
||||||
# Test change Mon Jan 5 12:43:01 PST 2026
|
# Test change Mon Jan 5 12:43:01 PST 2026
|
||||||
# Test commit hash persistence
|
# Test commit hash persistence
|
||||||
|
Test change Mon Jan 5 15:22:57 PST 2026
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ description = "Automated commit message generation service using local LLM infer
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tqftw-ml-service-base",
|
"lilith-ml-service-base",
|
||||||
"httpx>=0.27.0",
|
"httpx>=0.27.0",
|
||||||
"pydantic>=2.0",
|
"pydantic>=2.0",
|
||||||
"pyyaml>=6.0",
|
"pyyaml>=6.0",
|
||||||
|
|
@ -13,6 +13,9 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
model-boss = [
|
||||||
|
"lilith-model-boss>=0.1.0",
|
||||||
|
]
|
||||||
dev = [
|
dev = [
|
||||||
"pytest>=8.0",
|
"pytest>=8.0",
|
||||||
"pytest-asyncio>=0.23",
|
"pytest-asyncio>=0.23",
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -6,7 +6,7 @@ from datetime import datetime
|
||||||
|
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
|
|
||||||
from tqftw_ml_service_base import (
|
from lilith_ml_service_base import (
|
||||||
create_ml_service,
|
create_ml_service,
|
||||||
LifespanManager,
|
LifespanManager,
|
||||||
HealthChecker,
|
HealthChecker,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
from tqftw_ml_service_base import BaseServiceSettings
|
from lilith_ml_service_base import BaseServiceSettings
|
||||||
|
|
||||||
|
|
||||||
class AutoCommitSettings(BaseServiceSettings):
|
class AutoCommitSettings(BaseServiceSettings):
|
||||||
|
|
@ -122,6 +122,20 @@ class AutoCommitSettings(BaseServiceSettings):
|
||||||
description="Cycles between health checks (0 = check every cycle)",
|
description="Cycles between health checks (0 = check every cycle)",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Model-boss integration for auto-loading LLM
|
||||||
|
llama_fast_model_id: str = Field(
|
||||||
|
default="ministral-3b-instruct",
|
||||||
|
description="Model ID for fast commit message generation (resolved via model-boss)",
|
||||||
|
)
|
||||||
|
llama_reasoning_model_id: str | None = Field(
|
||||||
|
default=None,
|
||||||
|
description="Optional model ID for reasoning tasks (resolved via model-boss)",
|
||||||
|
)
|
||||||
|
use_model_boss: bool = Field(
|
||||||
|
default=True,
|
||||||
|
description="Use model-boss to resolve model paths before starting llama-service",
|
||||||
|
)
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
log_file: Path = Field(
|
log_file: Path = Field(
|
||||||
default=Path("/tmp/auto-commit.log"),
|
default=Path("/tmp/auto-commit.log"),
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -84,13 +84,14 @@ async def invoke_claude_for_recovery(
|
||||||
if returncode == 0:
|
if returncode == 0:
|
||||||
logger.info(f"Claude recovery succeeded for {repo.name}")
|
logger.info(f"Claude recovery succeeded for {repo.name}")
|
||||||
|
|
||||||
# Get the latest commit hash directly from git
|
# Get the latest commit info directly from git
|
||||||
commit_hash = await _get_latest_commit_hash(repo)
|
commit_hash = await _get_latest_commit_hash(repo)
|
||||||
|
commit_message = await _get_latest_commit_message(repo) if commit_hash else None
|
||||||
|
|
||||||
return RecoveryResult(
|
return RecoveryResult(
|
||||||
success=True,
|
success=True,
|
||||||
commit_hash=commit_hash,
|
commit_hash=commit_hash,
|
||||||
message=f"Recovered by Claude: {short_error}",
|
message=commit_message or f"Recovered by Claude: {short_error}",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.error(f"Claude recovery failed for {repo.name}: {stderr}")
|
logger.error(f"Claude recovery failed for {repo.name}: {stderr}")
|
||||||
|
|
@ -155,3 +156,39 @@ async def _get_latest_commit_hash(repo: "Repository") -> str | None:
|
||||||
logger.warning(f"Failed to get commit hash from {repo.name}: {e}")
|
logger.warning(f"Failed to get commit hash from {repo.name}: {e}")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def _get_latest_commit_message(repo: "Repository") -> str | None:
|
||||||
|
"""Get the latest commit message (subject line) from the repository.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
repo: Repository to query
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Latest commit message or None if failed
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
proc = await asyncio.create_subprocess_exec(
|
||||||
|
"git",
|
||||||
|
"log",
|
||||||
|
"-1",
|
||||||
|
"--format=%s",
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE,
|
||||||
|
cwd=str(repo.path),
|
||||||
|
)
|
||||||
|
|
||||||
|
stdout_bytes, _ = await asyncio.wait_for(
|
||||||
|
proc.communicate(),
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
|
||||||
|
if proc.returncode == 0 and stdout_bytes:
|
||||||
|
commit_message = stdout_bytes.decode().strip()
|
||||||
|
logger.debug(f"Got commit message '{commit_message[:50]}...' from {repo.name}")
|
||||||
|
return commit_message
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to get commit message from {repo.name}: {e}")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -48,6 +48,9 @@ class CommitDaemon:
|
||||||
lock_file=settings.llama_service_lock_file,
|
lock_file=settings.llama_service_lock_file,
|
||||||
startup_timeout=settings.llama_service_startup_timeout,
|
startup_timeout=settings.llama_service_startup_timeout,
|
||||||
health_check_timeout=5.0,
|
health_check_timeout=5.0,
|
||||||
|
fast_model_id=settings.llama_fast_model_id,
|
||||||
|
reasoning_model_id=settings.llama_reasoning_model_id,
|
||||||
|
use_model_boss=settings.use_model_boss,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.warning("Service manager disabled (autostart=False)")
|
logger.warning("Service manager disabled (autostart=False)")
|
||||||
|
|
@ -388,7 +391,7 @@ class CommitDaemon:
|
||||||
|
|
||||||
if health == ServiceHealth.UNREACHABLE:
|
if health == ServiceHealth.UNREACHABLE:
|
||||||
logger.info("Llama service unreachable, attempting to start...")
|
logger.info("Llama service unreachable, attempting to start...")
|
||||||
started = await self.service_manager.start_service()
|
started = await self.service_manager.ensure_service_available()
|
||||||
if started:
|
if started:
|
||||||
self._service_crashed = False
|
self._service_crashed = False
|
||||||
self._service_health = ServiceHealth.HEALTHY
|
self._service_health = ServiceHealth.HEALTHY
|
||||||
|
|
@ -398,8 +401,9 @@ class CommitDaemon:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if health == ServiceHealth.DEGRADED:
|
if health == ServiceHealth.DEGRADED:
|
||||||
logger.warning("Llama service is degraded")
|
logger.warning("Llama service is degraded but functional")
|
||||||
return False
|
# Degraded (e.g., CPU-only mode) is acceptable - commits can proceed
|
||||||
|
return True
|
||||||
|
|
||||||
# Service is healthy
|
# Service is healthy
|
||||||
self._service_crashed = False
|
self._service_crashed = False
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,45 @@ from ..recovery import ErrorHandler
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Infrastructure error patterns that Claude cannot fix
|
||||||
|
_NON_RECOVERABLE_PATTERNS = [
|
||||||
|
"couldn't find remote ref",
|
||||||
|
"remote not found",
|
||||||
|
"repository not found",
|
||||||
|
"connection refused",
|
||||||
|
"network is unreachable",
|
||||||
|
"could not resolve host",
|
||||||
|
"authentication failed",
|
||||||
|
"permission denied",
|
||||||
|
"unable to access",
|
||||||
|
"fatal: could not read",
|
||||||
|
"no such device or address",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _is_recoverable_error(error: str) -> bool:
|
||||||
|
"""Determine if an error can potentially be recovered by Claude.
|
||||||
|
|
||||||
|
Claude can help with:
|
||||||
|
- Merge conflicts
|
||||||
|
- Pre-commit hook failures
|
||||||
|
- Rebase conflicts
|
||||||
|
|
||||||
|
Claude cannot help with:
|
||||||
|
- Network/connectivity issues
|
||||||
|
- Authentication failures
|
||||||
|
- Missing remotes/refs
|
||||||
|
- Permission issues
|
||||||
|
|
||||||
|
Args:
|
||||||
|
error: The error message to analyze
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if the error might be recoverable, False for infrastructure errors
|
||||||
|
"""
|
||||||
|
error_lower = error.lower()
|
||||||
|
return not any(pattern in error_lower for pattern in _NON_RECOVERABLE_PATTERNS)
|
||||||
|
|
||||||
|
|
||||||
class CommitProcessor:
|
class CommitProcessor:
|
||||||
"""Processes commits for a single repository."""
|
"""Processes commits for a single repository."""
|
||||||
|
|
@ -313,6 +352,18 @@ class CommitProcessor:
|
||||||
) -> RepoProcessResult:
|
) -> RepoProcessResult:
|
||||||
"""Attempt recovery using error handler (Claude fallback)."""
|
"""Attempt recovery using error handler (Claude fallback)."""
|
||||||
if self.error_handler and self.settings.claude_fallback_enabled:
|
if self.error_handler and self.settings.claude_fallback_enabled:
|
||||||
|
# Check if this is an infrastructure error Claude can't fix
|
||||||
|
if not _is_recoverable_error(error):
|
||||||
|
logger.warning(
|
||||||
|
f"Skipping Claude recovery for {repo.name}: "
|
||||||
|
f"infrastructure error cannot be auto-resolved: {error[:100]}"
|
||||||
|
)
|
||||||
|
return RepoProcessResult(
|
||||||
|
repo_name=repo.name,
|
||||||
|
status=status,
|
||||||
|
error=error,
|
||||||
|
)
|
||||||
|
|
||||||
logger.info(f"Attempting recovery for {repo.name} via error handler")
|
logger.info(f"Attempting recovery for {repo.name} via error handler")
|
||||||
recovery_result = await self.error_handler.handle(repo, error)
|
recovery_result = await self.error_handler.handle(repo, error)
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -46,6 +46,9 @@ class LlamaServiceManager:
|
||||||
lock_file: Path | None = None,
|
lock_file: Path | None = None,
|
||||||
startup_timeout: float = 30.0,
|
startup_timeout: float = 30.0,
|
||||||
health_check_timeout: float = 5.0,
|
health_check_timeout: float = 5.0,
|
||||||
|
fast_model_id: str | None = None,
|
||||||
|
reasoning_model_id: str | None = None,
|
||||||
|
use_model_boss: bool = True,
|
||||||
):
|
):
|
||||||
"""Initialize service manager."""
|
"""Initialize service manager."""
|
||||||
self.service_url = service_url
|
self.service_url = service_url
|
||||||
|
|
@ -53,8 +56,13 @@ class LlamaServiceManager:
|
||||||
self._lock_file = lock_file or Path.home() / ".config/commits/llama-service.lock"
|
self._lock_file = lock_file or Path.home() / ".config/commits/llama-service.lock"
|
||||||
self._startup_timeout = startup_timeout
|
self._startup_timeout = startup_timeout
|
||||||
self._health_check_timeout = health_check_timeout
|
self._health_check_timeout = health_check_timeout
|
||||||
|
self._fast_model_id = fast_model_id
|
||||||
|
self._reasoning_model_id = reasoning_model_id
|
||||||
|
self._use_model_boss = use_model_boss
|
||||||
self._spawned_pid: int | None = None
|
self._spawned_pid: int | None = None
|
||||||
self._lock_fd: int | None = None
|
self._lock_fd: int | None = None
|
||||||
|
self._resolved_fast_model_path: str | None = None
|
||||||
|
self._resolved_reasoning_model_path: str | None = None
|
||||||
|
|
||||||
async def ensure_service_available(self) -> bool:
|
async def ensure_service_available(self) -> bool:
|
||||||
"""Ensure service is available, starting if necessary."""
|
"""Ensure service is available, starting if necessary."""
|
||||||
|
|
@ -66,7 +74,42 @@ class LlamaServiceManager:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logger.info("Llama service unreachable, attempting to start...")
|
logger.info("Llama service unreachable, attempting to start...")
|
||||||
return await self.start_service()
|
|
||||||
|
try:
|
||||||
|
# Resolve model paths via model-boss before starting
|
||||||
|
if self._use_model_boss and self._fast_model_id:
|
||||||
|
await self._resolve_model_paths()
|
||||||
|
|
||||||
|
return await self.start_service()
|
||||||
|
except ServiceStartError as e:
|
||||||
|
logger.error(f"Failed to start llama-service: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def _resolve_model_paths(self) -> None:
|
||||||
|
"""Resolve model IDs to paths via model-boss.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ServiceStartError: If model resolution fails
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from lilith_model_boss import ensure_model
|
||||||
|
|
||||||
|
if self._fast_model_id and not self._resolved_fast_model_path:
|
||||||
|
logger.info(f"Resolving fast model via model-boss: {self._fast_model_id}")
|
||||||
|
self._resolved_fast_model_path = ensure_model(self._fast_model_id)
|
||||||
|
logger.info(f"Resolved fast model path: {self._resolved_fast_model_path}")
|
||||||
|
|
||||||
|
if self._reasoning_model_id and not self._resolved_reasoning_model_path:
|
||||||
|
logger.info(f"Resolving reasoning model via model-boss: {self._reasoning_model_id}")
|
||||||
|
self._resolved_reasoning_model_path = ensure_model(self._reasoning_model_id)
|
||||||
|
logger.info(f"Resolved reasoning model path: {self._resolved_reasoning_model_path}")
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
raise ServiceStartError(
|
||||||
|
"model-boss not installed. Install with: pip install auto-commit-service[model-boss]"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
raise ServiceStartError(f"Failed to resolve model paths: {e}")
|
||||||
|
|
||||||
async def start_service(self) -> bool:
|
async def start_service(self) -> bool:
|
||||||
"""Start llama service subprocess."""
|
"""Start llama service subprocess."""
|
||||||
|
|
@ -196,19 +239,50 @@ class LlamaServiceManager:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def _spawn_service(self) -> asyncio.subprocess.Process:
|
async def _spawn_service(self) -> asyncio.subprocess.Process:
|
||||||
"""Spawn service as background subprocess."""
|
"""Spawn service as background subprocess.
|
||||||
cmd = [sys.executable, "-m", "tqftw_llama_service"]
|
|
||||||
|
Raises:
|
||||||
|
ServiceStartError: If no model paths are configured
|
||||||
|
"""
|
||||||
|
cmd = [sys.executable, "-m", "lilith_llama_service"]
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
|
|
||||||
# Enable mock mode if no real models configured
|
# Use resolved model paths from model-boss if available
|
||||||
if "LLAMA_SERVICE_FAST_MODEL_PATH" not in env and "LLAMA_SERVICE_REASONING_MODEL_PATH" not in env:
|
has_model_paths = False
|
||||||
env["LLAMA_SERVICE_MOCK_MODE"] = "true"
|
|
||||||
|
if self._resolved_fast_model_path:
|
||||||
|
env["LLAMA_SERVICE_FAST_MODEL_PATH"] = self._resolved_fast_model_path
|
||||||
|
has_model_paths = True
|
||||||
|
logger.info(f"Using fast model: {self._resolved_fast_model_path}")
|
||||||
|
|
||||||
|
if self._resolved_reasoning_model_path:
|
||||||
|
env["LLAMA_SERVICE_REASONING_MODEL_PATH"] = self._resolved_reasoning_model_path
|
||||||
|
has_model_paths = True
|
||||||
|
logger.info(f"Using reasoning model: {self._resolved_reasoning_model_path}")
|
||||||
|
|
||||||
|
# Fall back to environment variables if set
|
||||||
|
if not has_model_paths:
|
||||||
|
if "LLAMA_SERVICE_FAST_MODEL_PATH" in env or "LLAMA_SERVICE_REASONING_MODEL_PATH" in env:
|
||||||
|
has_model_paths = True
|
||||||
|
logger.info("Using model paths from environment variables")
|
||||||
|
|
||||||
|
# Fail if no models are configured - do not fall back to mock mode
|
||||||
|
if not has_model_paths:
|
||||||
|
raise ServiceStartError(
|
||||||
|
"No model paths configured. Either:\n"
|
||||||
|
" 1. Install model-boss: pip install auto-commit-service[model-boss]\n"
|
||||||
|
" 2. Set LLAMA_SERVICE_FAST_MODEL_PATH environment variable\n"
|
||||||
|
" 3. Disable llama_service_autostart in config"
|
||||||
|
)
|
||||||
|
|
||||||
log_file = self._pid_file.parent / "llama-service.log"
|
log_file = self._pid_file.parent / "llama-service.log"
|
||||||
log_file.parent.mkdir(parents=True, exist_ok=True)
|
log_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
with open(log_file, "a") as log:
|
with open(log_file, "a") as log:
|
||||||
log.write(f"\n=== Service started at {time.ctime()} ===\n")
|
log.write(f"\n=== Service started at {time.ctime()} ===\n")
|
||||||
|
log.write(f"Fast model: {env.get('LLAMA_SERVICE_FAST_MODEL_PATH', 'not set')}\n")
|
||||||
|
log.write(f"Reasoning model: {env.get('LLAMA_SERVICE_REASONING_MODEL_PATH', 'not set')}\n")
|
||||||
|
|
||||||
process = await asyncio.create_subprocess_exec(
|
process = await asyncio.create_subprocess_exec(
|
||||||
*cmd,
|
*cmd,
|
||||||
env=env,
|
env=env,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue