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.
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: bogo-llm-tools
3
+ Version: 0.1.0
4
+ Author-email: Bogomil Filipov <bogomil.filipov@proton.me>
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: patchright<2.0.0,>=1.60.1
7
+ Requires-Dist: trafilatura<3.0.0,>=2.1.0
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: bogo-llm-tools
3
+ Version: 0.1.0
4
+ Author-email: Bogomil Filipov <bogomil.filipov@proton.me>
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: patchright<2.0.0,>=1.60.1
7
+ Requires-Dist: trafilatura<3.0.0,>=2.1.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,2 @@
1
+ patchright<2.0.0,>=1.60.1
2
+ trafilatura<3.0.0,>=2.1.0
@@ -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"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+