All checks were successful
Deploy mindnet to llm-node / deploy (push) Successful in 4s
161 lines
4.8 KiB
Python
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())
|