bogo-llm-tools 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bogo_llm_tools-0.1.0/PKG-INFO +7 -0
- bogo_llm_tools-0.1.0/bogo_llm_tools.egg-info/PKG-INFO +7 -0
- bogo_llm_tools-0.1.0/bogo_llm_tools.egg-info/SOURCES.txt +9 -0
- bogo_llm_tools-0.1.0/bogo_llm_tools.egg-info/dependency_links.txt +1 -0
- bogo_llm_tools-0.1.0/bogo_llm_tools.egg-info/requires.txt +2 -0
- bogo_llm_tools-0.1.0/bogo_llm_tools.egg-info/top_level.txt +1 -0
- bogo_llm_tools-0.1.0/experiments/experiment_agent_tool.py +115 -0
- bogo_llm_tools-0.1.0/experiments/experiment_crawler.py +11 -0
- bogo_llm_tools-0.1.0/experiments/logger.py +33 -0
- bogo_llm_tools-0.1.0/pyproject.toml +26 -0
- bogo_llm_tools-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
bogo_llm_tools.egg-info/PKG-INFO
|
|
3
|
+
bogo_llm_tools.egg-info/SOURCES.txt
|
|
4
|
+
bogo_llm_tools.egg-info/dependency_links.txt
|
|
5
|
+
bogo_llm_tools.egg-info/requires.txt
|
|
6
|
+
bogo_llm_tools.egg-info/top_level.txt
|
|
7
|
+
experiments/experiment_agent_tool.py
|
|
8
|
+
experiments/experiment_crawler.py
|
|
9
|
+
experiments/logger.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
experiments
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Experiment: Using an Agent with a Tool (CrawlerApi) and Ollama.
|
|
3
|
+
|
|
4
|
+
This script demonstrates how to use an LLM agent with a tool definition
|
|
5
|
+
to scrape a webpage using a locally hosted Ollama model.
|
|
6
|
+
"""
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
from logger import setup_logger
|
|
11
|
+
|
|
12
|
+
from llm_tools.crawler.tool import TOOL_SCHEMA, run_tool
|
|
13
|
+
|
|
14
|
+
logger = setup_logger(__name__, f"{__file__}.log")
|
|
15
|
+
|
|
16
|
+
# Configuration for Ollama
|
|
17
|
+
OLLAMA_URL = "http://localhost:11434/api/chat"
|
|
18
|
+
MODEL_NAME = "qwen3-coder-next:q4_K_M"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_llm_response(messages: list, tools: list = None) -> dict:
|
|
22
|
+
"""
|
|
23
|
+
Simulate an LLM API call to Ollama.
|
|
24
|
+
In a real scenario, this would make an HTTP request to the Ollama API.
|
|
25
|
+
For this experiment, we'll simulate the response.
|
|
26
|
+
"""
|
|
27
|
+
# This is a placeholder for the actual API call.
|
|
28
|
+
# You would use `requests` to call the Ollama API here.
|
|
29
|
+
# Example:
|
|
30
|
+
|
|
31
|
+
logger.info(f"Calling LLM API with messages: {messages}")
|
|
32
|
+
|
|
33
|
+
response = requests.post(
|
|
34
|
+
OLLAMA_URL,
|
|
35
|
+
json={
|
|
36
|
+
"model": MODEL_NAME,
|
|
37
|
+
"messages": messages,
|
|
38
|
+
"tools": tools,
|
|
39
|
+
"stream": False,
|
|
40
|
+
# "options": {"num_ctx": 32768},
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
resp = response.json()
|
|
44
|
+
logger.info(resp)
|
|
45
|
+
return resp
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def run_agent_with_tool():
|
|
49
|
+
logger.info("=== Agent with Tool Experiment ===")
|
|
50
|
+
|
|
51
|
+
user_message = (
|
|
52
|
+
"Find all listings and determine the average price on this webpage: "
|
|
53
|
+
"https://www.ebay.co.uk/sch/i.html?_nkw=h255&_sacat=58058&_from=R40&_trksid=m570.l1313"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
messages = [{"role": "user", "content": user_message}]
|
|
57
|
+
|
|
58
|
+
# First call: LLM decides whether to use the tool
|
|
59
|
+
response = get_llm_response(messages, tools=[TOOL_SCHEMA])
|
|
60
|
+
logger.info(f"First LLM response: {response}")
|
|
61
|
+
|
|
62
|
+
# Check if the LLM wants to call a tool
|
|
63
|
+
if "tool_calls" in response.get("message", {}):
|
|
64
|
+
tool_calls = response["message"]["tool_calls"]
|
|
65
|
+
|
|
66
|
+
for tool_call in tool_calls:
|
|
67
|
+
func = tool_call["function"]
|
|
68
|
+
tool_name = func["name"]
|
|
69
|
+
tool_args = func["arguments"]
|
|
70
|
+
|
|
71
|
+
logger.info(f"Calling tool: {tool_name} with args: {tool_args}")
|
|
72
|
+
|
|
73
|
+
if tool_name == "scrape_webpage":
|
|
74
|
+
tool_result = run_tool(tool_args)
|
|
75
|
+
logger.info(f"Tool result: {tool_result}")
|
|
76
|
+
|
|
77
|
+
# Append the assistant's tool call and the tool's result to the history
|
|
78
|
+
messages.append({
|
|
79
|
+
"role": "assistant",
|
|
80
|
+
"content": "",
|
|
81
|
+
"tool_calls": [tool_call]
|
|
82
|
+
})
|
|
83
|
+
messages.append({
|
|
84
|
+
"role": "tool",
|
|
85
|
+
"name": tool_name,
|
|
86
|
+
"content": json.dumps(tool_result)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
# Second call: LLM uses the tool result to form the final answer
|
|
90
|
+
final_response = get_llm_response(messages)
|
|
91
|
+
logger.debug(f"Final LLM response: {final_response}")
|
|
92
|
+
|
|
93
|
+
# --- EXTRACT THE FINAL ANSWER ---
|
|
94
|
+
final_message = final_response.get("message", {})
|
|
95
|
+
final_content = final_message.get("content", "")
|
|
96
|
+
|
|
97
|
+
if final_content:
|
|
98
|
+
print("\n=== FINAL ANSWER ===\n")
|
|
99
|
+
print(final_content)
|
|
100
|
+
logger.info(f"Final answer: {final_content}")
|
|
101
|
+
else:
|
|
102
|
+
# Sometimes the model may not return content; log warning
|
|
103
|
+
logger.warning("Final response has no content. Raw response:")
|
|
104
|
+
logger.warning(json.dumps(final_response, indent=2))
|
|
105
|
+
print("No final answer returned by the model.")
|
|
106
|
+
else:
|
|
107
|
+
# No tool was called; just print the direct response
|
|
108
|
+
direct_answer = response.get("message", {}).get("content", "")
|
|
109
|
+
print("\n=== DIRECT ANSWER ===\n")
|
|
110
|
+
print(direct_answer)
|
|
111
|
+
logger.info(f"Direct answer (no tool): {direct_answer}")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
if __name__ == "__main__":
|
|
115
|
+
run_agent_with_tool()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from llm_tools.crawler import CrawlerApi
|
|
2
|
+
|
|
3
|
+
import nest_asyncio
|
|
4
|
+
nest_asyncio.apply()
|
|
5
|
+
crawler = CrawlerApi()
|
|
6
|
+
crawler.start()
|
|
7
|
+
full_url = 'https://www.ebay.co.uk/sch/i.html?_nkw=gmktec+k12&_sacat=58058&_from=R40&_trksid=p2334524.m570.l1313'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
result = crawler.scrape(full_url)
|
|
11
|
+
print(result)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
def setup_logger(name: str = "my_app", log_file: str = "app.log", level=logging.INFO):
|
|
4
|
+
"""
|
|
5
|
+
Set up a logger that writes to a file.
|
|
6
|
+
|
|
7
|
+
Args:
|
|
8
|
+
name: Logger name (usually __name__)
|
|
9
|
+
log_file: Path to the log file
|
|
10
|
+
level: Logging level (e.g., logging.DEBUG, logging.INFO)
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
logging.Logger instance
|
|
14
|
+
"""
|
|
15
|
+
logger = logging.getLogger(name)
|
|
16
|
+
logger.setLevel(level)
|
|
17
|
+
|
|
18
|
+
# Avoid duplicate handlers if called multiple times
|
|
19
|
+
if not logger.handlers:
|
|
20
|
+
# Create file handler
|
|
21
|
+
file_handler = logging.FileHandler(log_file)
|
|
22
|
+
file_handler.setLevel(level)
|
|
23
|
+
|
|
24
|
+
# Create formatter
|
|
25
|
+
formatter = logging.Formatter(
|
|
26
|
+
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
27
|
+
datefmt='%Y-%m-%d %H:%M:%S'
|
|
28
|
+
)
|
|
29
|
+
file_handler.setFormatter(formatter)
|
|
30
|
+
|
|
31
|
+
logger.addHandler(file_handler)
|
|
32
|
+
|
|
33
|
+
return logger
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "bogo-llm-tools"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Bogomil Filipov",email = "bogomil.filipov@proton.me"}
|
|
7
|
+
]
|
|
8
|
+
requires-python = ">=3.12"
|
|
9
|
+
dependencies = [
|
|
10
|
+
"patchright (>=1.60.1,<2.0.0)",
|
|
11
|
+
"trafilatura (>=2.1.0,<3.0.0)",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[dependency-groups]
|
|
15
|
+
dev = [
|
|
16
|
+
"pytest",
|
|
17
|
+
"pytest-asyncio (>=1.4.0,<2.0.0)"
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["setuptools>=61.0"]
|
|
23
|
+
build-backend = "setuptools.build_meta"
|
|
24
|
+
|
|
25
|
+
[tool.pytest.ini_options]
|
|
26
|
+
asyncio_mode = "auto"
|