Add local LLM configuration and tool demo
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
# Backups locales: pueden contener claves y secretos
|
||||
ubuntu-config-*.tar.gz
|
||||
@@ -0,0 +1,10 @@
|
||||
model_list:
|
||||
- model_name: qwen2.5-coder:14b
|
||||
litellm_params:
|
||||
model: ollama_chat/qwen2.5-coder:14b
|
||||
api_base: http://127.0.0.1:11434
|
||||
max_tokens: 4096
|
||||
|
||||
litellm_settings:
|
||||
drop_params: true
|
||||
json_to_tool_call: true
|
||||
@@ -0,0 +1,106 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
SYSTEM_PROMPT = (
|
||||
"You are a helpful assistant. "
|
||||
"When outputting Windows file paths in JSON arguments, you must strictly escape all backslashes "
|
||||
"(for example: C:\\\\Users\\\\name\\\\file.txt). "
|
||||
"If you need to read a file, emit a tool call with the path field."
|
||||
)
|
||||
|
||||
|
||||
def read_file(path: str) -> str:
|
||||
"""Read a file from disk and return its contents."""
|
||||
file_path = Path(path)
|
||||
try:
|
||||
return file_path.read_text(encoding="utf-8")
|
||||
except FileNotFoundError:
|
||||
return f"ERROR: File not found: {path}"
|
||||
except Exception as exc: # pragma: no cover - demo only
|
||||
return f"ERROR: {type(exc).__name__}: {exc}"
|
||||
|
||||
|
||||
def sanitize_tool_arguments(raw_arguments: str):
|
||||
"""Repair malformed JSON emitted by the model when Windows paths are not escaped."""
|
||||
try:
|
||||
return json.loads(raw_arguments)
|
||||
except json.JSONDecodeError:
|
||||
repaired = raw_arguments.replace("\\", "\\\\")
|
||||
return json.loads(repaired)
|
||||
|
||||
|
||||
def handle_tool_call(response: dict) -> dict:
|
||||
"""Detect a tool call in the LLM response and execute it locally."""
|
||||
tool_calls = response.get("tool_calls") or response.get("function_call")
|
||||
|
||||
if tool_calls is None:
|
||||
return {"status": "final_response", "content": response}
|
||||
|
||||
if isinstance(tool_calls, dict):
|
||||
tool_calls = [tool_calls]
|
||||
|
||||
for tool_call in tool_calls:
|
||||
function_data = tool_call.get("function", tool_call)
|
||||
name = function_data.get("name")
|
||||
arguments = function_data.get("arguments", {})
|
||||
|
||||
if isinstance(arguments, str):
|
||||
try:
|
||||
arguments = json.loads(arguments)
|
||||
except json.JSONDecodeError:
|
||||
try:
|
||||
arguments = sanitize_tool_arguments(arguments)
|
||||
except json.JSONDecodeError:
|
||||
return {
|
||||
"status": "invalid_arguments",
|
||||
"raw_arguments": arguments,
|
||||
"message": "The model emitted malformed JSON. Ensure backslashes are escaped.",
|
||||
}
|
||||
|
||||
if name == "read_file":
|
||||
file_path = arguments.get("path")
|
||||
content = read_file(file_path)
|
||||
return {
|
||||
"status": "tool_result",
|
||||
"tool_call_id": tool_call.get("id"),
|
||||
"content": content,
|
||||
}
|
||||
|
||||
return {"status": "unsupported_tool_call", "raw": response}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# A valid OpenAI-style tool call with a Windows path escaped correctly.
|
||||
valid_response = {
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_read_001",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "read_file",
|
||||
"arguments": '{"path": "c:\\Workspace\\llm-server-setup\\README.md"}',
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
# This reproduces the common bug: malformed Windows path in JSON.
|
||||
broken_response = {
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_read_002",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "read_file",
|
||||
"arguments": '{"path": "c:\Workspace\llm-server-setup\README.md"}',
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
result = handle_tool_call(valid_response)
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
|
||||
print("\n--- malformed-path fallback sample ---")
|
||||
fallback = handle_tool_call(broken_response)
|
||||
print(json.dumps(fallback, ensure_ascii=False, indent=2))
|
||||
Reference in New Issue
Block a user