Compare commits

...

3 Commits

Author SHA1 Message Date
Alexander Whitestone
f2e1366795 WIP: Gemini Code progress on #1014
Automated salvage commit — agent session ended (exit 124).
Work in progress, may need continuation.
2026-03-23 22:24:10 -04:00
Alexander Whitestone
15fee6bef2 feat: add button to update ollama models
Adds a button to the models page to trigger an update of the
local Ollama models.

Refs #1014
2026-03-23 22:17:28 -04:00
Alexander Whitestone
b6f8f7d67b WIP: Gemini Code progress on #1014
Some checks failed
Tests / lint (pull_request) Failing after 33s
Tests / test (pull_request) Has been skipped
Automated salvage commit — agent session ended (exit 124).
Work in progress, may need continuation.
2026-03-23 14:37:31 -04:00
9 changed files with 100 additions and 14 deletions

75
scripts/update_ollama_models.py Executable file
View File

@@ -0,0 +1,75 @@
import subprocess
import json
import os
import glob
def get_models_from_modelfiles():
models = set()
modelfiles = glob.glob("Modelfile.*")
for modelfile in modelfiles:
with open(modelfile, 'r') as f:
for line in f:
if line.strip().startswith("FROM"):
parts = line.strip().split()
if len(parts) > 1:
model_name = parts[1]
# Only consider models that are not local file paths
if not model_name.startswith('/') and not model_name.startswith('~') and not model_name.endswith('.gguf'):
models.add(model_name)
break # Only take the first FROM in each Modelfile
return sorted(list(models))
def update_ollama_model(model_name):
print(f"Checking for updates for model: {model_name}")
try:
# Run ollama pull command
process = subprocess.run(
["ollama", "pull", model_name],
capture_output=True,
text=True,
check=True,
timeout=900 # 15 minutes
)
output = process.stdout
print(f"Output for {model_name}:\n{output}")
# Basic check to see if an update happened.
# Ollama pull output will contain "pulling" or "downloading" if an update is in progress
# and "success" if it completed. If the model is already up to date, it says "already up to date".
if "pulling" in output or "downloading" in output:
print(f"Model {model_name} was updated.")
return True
elif "already up to date" in output:
print(f"Model {model_name} is already up to date.")
return False
else:
print(f"Unexpected output for {model_name}, assuming no update: {output}")
return False
except subprocess.CalledProcessError as e:
print(f"Error updating model {model_name}: {e}")
print(f"Stderr: {e.stderr}")
return False
except FileNotFoundError:
print("Error: 'ollama' command not found. Please ensure Ollama is installed and in your PATH.")
return False
def main():
models_to_update = get_models_from_modelfiles()
print(f"Identified models to check for updates: {models_to_update}")
updated_models = []
for model in models_to_update:
if update_ollama_model(model):
updated_models.append(model)
if updated_models:
print("\nSuccessfully updated the following models:")
for model in updated_models:
print(f"- {model}")
else:
print("\nNo models were updated.")
if __name__ == "__main__":
main()

View File

@@ -5,6 +5,7 @@ to swarm agents. Inspired by OpenClaw-RL's multi-model orchestration.
""" """
import logging import logging
import subprocess
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -59,6 +60,23 @@ class SetActiveRequest(BaseModel):
# ── API endpoints ───────────────────────────────────────────────────────────── # ── API endpoints ─────────────────────────────────────────────────────────────
@api_router.post("/update-ollama")
async def update_ollama_models():
"""Trigger the Ollama model update script."""
logger.info("Ollama model update triggered")
script_path = Path(__file__).parent.parent.parent.parent / "scripts" / "update_ollama_models.py"
try:
subprocess.Popen(
["python", str(script_path)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
return {"message": "Ollama model update started in the background."}
except Exception as e:
logger.error(f"Failed to start Ollama model update: {e}")
raise HTTPException(status_code=500, detail="Failed to start model update script.") from e
@api_router.get("") @api_router.get("")
async def list_models(role: str | None = None) -> dict[str, Any]: async def list_models(role: str | None = None) -> dict[str, Any]:
"""List all registered custom models.""" """List all registered custom models."""

View File

@@ -53,7 +53,12 @@
<!-- Registered Models --> <!-- Registered Models -->
<div class="mc-section" style="margin-top: 1.5rem;"> <div class="mc-section" style="margin-top: 1.5rem;">
<h2>Registered Models</h2> <div style="display: flex; justify-content: space-between; align-items: center;">
<h2>Registered Models</h2>
<button class="mc-btn" hx-post="/api/v1/models/update-ollama" hx-swap="none">
Update Ollama Models
</button>
</div>
{% if models %} {% if models %}
<table class="mc-table"> <table class="mc-table">
<thead> <thead>

View File

@@ -13,8 +13,8 @@ from dataclasses import dataclass
import httpx import httpx
from config import settings from config import settings
from timmy.research_tools import get_llm_client, google_web_search
from timmy.research_triage import triage_research_report from timmy.research_triage import triage_research_report
from timmy.research_tools import google_web_search, get_llm_client
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@@ -6,7 +6,6 @@ import logging
import os import os
from typing import Any from typing import Any
from config import settings
from serpapi import GoogleSearch from serpapi import GoogleSearch
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@@ -10,14 +10,12 @@ from __future__ import annotations
import json import json
import socket import socket
from pathlib import Path
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
import pytest import pytest
from integrations.bannerlord.gabs_client import GabsClient, GabsError from integrations.bannerlord.gabs_client import GabsClient, GabsError
# ── GabsClient unit tests ───────────────────────────────────────────────────── # ── GabsClient unit tests ─────────────────────────────────────────────────────

View File

@@ -9,10 +9,8 @@ import json
from pathlib import Path from pathlib import Path
import pytest import pytest
import scripts.export_trajectories as et import scripts.export_trajectories as et
# ── Fixtures ────────────────────────────────────────────────────────────────── # ── Fixtures ──────────────────────────────────────────────────────────────────

View File

@@ -4,8 +4,6 @@ from __future__ import annotations
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from timmy.dispatcher import ( from timmy.dispatcher import (
AGENT_REGISTRY, AGENT_REGISTRY,
AgentType, AgentType,
@@ -21,7 +19,6 @@ from timmy.dispatcher import (
wait_for_completion, wait_for_completion,
) )
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Agent registry # Agent registry
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

View File

@@ -9,19 +9,15 @@ Refs: #1105
from __future__ import annotations from __future__ import annotations
import json import json
import tempfile
from datetime import UTC, datetime, timedelta from datetime import UTC, datetime, timedelta
from pathlib import Path from pathlib import Path
import pytest
from timmy_automations.retrain.quality_filter import QualityFilter, TrajectoryQuality from timmy_automations.retrain.quality_filter import QualityFilter, TrajectoryQuality
from timmy_automations.retrain.retrain import RetrainOrchestrator from timmy_automations.retrain.retrain import RetrainOrchestrator
from timmy_automations.retrain.training_dataset import TrainingDataset from timmy_automations.retrain.training_dataset import TrainingDataset
from timmy_automations.retrain.training_log import CycleMetrics, TrainingLog from timmy_automations.retrain.training_log import CycleMetrics, TrainingLog
from timmy_automations.retrain.trajectory_exporter import Trajectory, TrajectoryExporter from timmy_automations.retrain.trajectory_exporter import Trajectory, TrajectoryExporter
# ── Fixtures ───────────────────────────────────────────────────────────────── # ── Fixtures ─────────────────────────────────────────────────────────────────