mindnet/scripts/ollama_tool_runner.py
Lars e9532e8878
All checks were successful
Deploy mindnet to llm-node / deploy (push) Successful in 4s
script_Überprüfung und Kommentarheader
2025-12-28 10:40:28 +01:00

161 lines
4.8 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
FILE: scripts/ollama_tool_runner.py
VERSION: 2.1.0 (2025-12-15)
STATUS: Active
COMPATIBILITY: v2.9.1 (Post-WP14/WP-15b)
Zweck:
-------
Minimaler Tool-Caller für Ollama mit mindnet-Tools.
Führt Tool-Call-Loop für mindnet_query und mindnet_subgraph aus.
Funktionsweise:
---------------
1. Lädt Tool-Schema von /tools/ollama Endpoint
2. Sendet Query an Ollama mit Tool-Definitionen
3. Ollama entscheidet, welche Tools aufzurufen sind
4. Führt Tool-Calls aus:
- mindnet_query: Ruft /query Endpoint auf
- mindnet_subgraph: Ruft /graph Endpoint auf
5. Wiederholt bis keine weiteren Tool-Calls nötig sind
Ergebnis-Interpretation:
------------------------
- Ausgabe: Konversations-Verlauf mit Tool-Calls und Antworten
- Exit-Code 0: Erfolgreich
- Exit-Code 1: Fehler (z.B. API nicht erreichbar)
Verwendung:
-----------
- Testing von Tool-Integration
- Demo der Agent-Funktionalität
- Entwicklung neuer Tools
Hinweise:
---------
- Nutzt Ollama als LLM-Backend
- Tool-Schema wird von mindnet API bereitgestellt
- Unterstützt mehrere Tool-Calls in einer Konversation
Aufruf:
-------
python3 scripts/ollama_tool_runner.py "Frage an den Agenten"
Parameter:
----------
Keine CLI-Parameter. Query als erstes Argument.
Umgebungsvariablen:
-------------------
OLLAMA (Default: http://127.0.0.1:11434)
API_BASE (Default: http://127.0.0.1:8000)
MODEL (Default: llama3.1)
Änderungen:
-----------
v2.1.0 (2025-12-15): Dokumentation aktualisiert
v0.1.0 (2025-10-07): Initial Release
"""
from __future__ import annotations
import os, sys, json, requests
OLLAMA = os.getenv("OLLAMA", "http://127.0.0.1:11434")
API_BASE = os.getenv("API_BASE", "http://127.0.0.1:8000")
MODEL = os.getenv("MODEL", "llama3.1")
def load_tools():
r = requests.get(f"{API_BASE}/tools/ollama", timeout=30)
r.raise_for_status()
return r.json()["tools"]
def call_model(messages, tools):
payload = {
"model": MODEL,
"messages": messages,
"tools": tools,
"stream": False,
"tool_choice": "auto",
}
r = requests.post(f"{OLLAMA}/api/chat", json=payload, timeout=180)
r.raise_for_status()
return r.json()
def execute_tool(name: str, arguments: dict):
if name == "mindnet_query":
# mappe expand_depth/edge_types auf /query-Body
body = {
"mode": "hybrid",
"top_k": int(arguments.get("top_k", 10)),
"expand": {
"depth": int(arguments.get("expand_depth", 1)),
"edge_types": arguments.get("edge_types") or ["references","belongs_to","prev","next"],
},
"filters": arguments.get("filters"),
}
if "query_vector" in arguments:
body["query_vector"] = arguments["query_vector"]
if "query" in arguments:
body["query"] = arguments["query"]
r = requests.post(f"{API_BASE}/query", json=body, timeout=120)
r.raise_for_status()
return r.json()
if name == "mindnet_subgraph":
params = {
"depth": int(arguments.get("depth", 1)),
}
edge_types = arguments.get("edge_types")
if edge_types:
# mehrfacher Query-Param 'edge_types' wird von requests automatisch gebaut
params["edge_types"] = edge_types
note_id = arguments["note_id"]
r = requests.get(f"{API_BASE}/graph/{note_id}", params=params, timeout=60)
r.raise_for_status()
return r.json()
raise ValueError(f"Unbekanntes Tool: {name}")
def main():
if len(sys.argv) < 2:
print("Usage: python3 scripts/ollama_tool_runner.py \"Deine Frage\"")
return 2
user_prompt = sys.argv[1]
tools = load_tools()
messages = [{"role": "user", "content": user_prompt}]
while True:
resp = call_model(messages, tools)
msg = resp.get("message") or {}
tool_calls = msg.get("tool_calls") or []
if not tool_calls:
# finale Antwort
print("\n=== Antwort ===\n")
print(msg.get("content", "").strip())
return 0
# Führe Toolcalls nacheinander aus
for tc in tool_calls:
name = tc["function"]["name"]
args = tc["function"].get("arguments") or {}
if isinstance(args, str):
try: args = json.loads(args)
except Exception: args = {"raw": args}
print(f"[tool] {name}({args})")
result = execute_tool(name, args)
# Übergib Toolresultat ans Modell zurück
messages.append({"role": "tool", "content": json.dumps(result), "name": name})
# füttere auch die originale Antwort (falls enthalten) in den Verlauf
if msg.get("content"):
messages.append({"role": "assistant", "content": msg["content"]})
if __name__ == "__main__":
sys.exit(main())