lightmodel 0.2.0__tar.gz → 0.4.1__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.
- {lightmodel-0.2.0 → lightmodel-0.4.1}/LICENSE +1 -1
- lightmodel-0.4.1/MANIFEST.in +4 -0
- {lightmodel-0.2.0/src/lightmodel.egg-info → lightmodel-0.4.1}/PKG-INFO +39 -5
- lightmodel-0.4.1/README.md +75 -0
- lightmodel-0.4.1/SKILL.md +183 -0
- lightmodel-0.4.1/documentation.md +307 -0
- {lightmodel-0.2.0 → lightmodel-0.4.1}/pyproject.toml +6 -3
- lightmodel-0.4.1/src/lightmodel/SKILL.md +183 -0
- lightmodel-0.4.1/src/lightmodel/__init__.py +12 -0
- lightmodel-0.4.1/src/lightmodel/agent.py +161 -0
- lightmodel-0.4.1/src/lightmodel/documentation.md +307 -0
- lightmodel-0.4.1/src/lightmodel/exceptions.py +22 -0
- lightmodel-0.4.1/src/lightmodel/model.py +126 -0
- lightmodel-0.4.1/src/lightmodel/tools.py +161 -0
- {lightmodel-0.2.0 → lightmodel-0.4.1/src/lightmodel.egg-info}/PKG-INFO +39 -5
- {lightmodel-0.2.0 → lightmodel-0.4.1}/src/lightmodel.egg-info/SOURCES.txt +6 -0
- lightmodel-0.2.0/README.md +0 -41
- lightmodel-0.2.0/src/lightmodel/__init__.py +0 -7
- lightmodel-0.2.0/src/lightmodel/agent.py +0 -71
- lightmodel-0.2.0/src/lightmodel/model.py +0 -45
- lightmodel-0.2.0/src/lightmodel/tools.py +0 -61
- {lightmodel-0.2.0 → lightmodel-0.4.1}/setup.cfg +0 -0
- {lightmodel-0.2.0 → lightmodel-0.4.1}/src/lightmodel.egg-info/dependency_links.txt +0 -0
- {lightmodel-0.2.0 → lightmodel-0.4.1}/src/lightmodel.egg-info/requires.txt +0 -0
- {lightmodel-0.2.0 → lightmodel-0.4.1}/src/lightmodel.egg-info/top_level.txt +0 -0
- {lightmodel-0.2.0 → lightmodel-0.4.1}/tests/test_model.py +0 -0
- {lightmodel-0.2.0 → lightmodel-0.4.1}/tests/test_tools.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lightmodel
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Ultra-light autonomous AI agents for any OpenAI-compatible API
|
|
5
|
-
Author-email:
|
|
5
|
+
Author-email: PlimbAI <plimb.ai.contact@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
|
-
Keywords: llm,ai,agent,openai,nvidia-nim,tool-calling
|
|
7
|
+
Keywords: llm,ai,agent,openai,nvidia-nim,tool-calling,no-dependencies
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
10
|
Requires-Python: >=3.9
|
|
@@ -20,10 +20,18 @@ Dynamic: license-file
|
|
|
20
20
|
Ultra-light **autonomous AI agents** for any OpenAI-compatible API.
|
|
21
21
|
No `smolagents`, no `litellm`, no `openai` — just `requests`.
|
|
22
22
|
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- `LightModel`: HTTP client with automatic retry on transient errors
|
|
26
|
+
- `Agent`: autonomous tool-calling loop with parallel tools and callbacks
|
|
27
|
+
- Built-in tools: `web_search`, `visit_webpage`, `calculator`
|
|
28
|
+
- Extensible: add a tool by subclassing `Tool`
|
|
29
|
+
- Zero heavy dependencies
|
|
30
|
+
|
|
23
31
|
## Installation
|
|
24
32
|
|
|
25
33
|
pip install lightmodel
|
|
26
|
-
pip install lightmodel[search] # with beautifulsoup4 for web
|
|
34
|
+
pip install lightmodel[search] # with beautifulsoup4 for web tools
|
|
27
35
|
|
|
28
36
|
## Quick start
|
|
29
37
|
|
|
@@ -38,21 +46,47 @@ No `smolagents`, no `litellm`, no `openai` — just `requests`.
|
|
|
38
46
|
model=model,
|
|
39
47
|
tools=[SimpleDuckDuckGoSearchTool()],
|
|
40
48
|
system_prompt="You are a helpful research assistant.",
|
|
49
|
+
max_steps=6,
|
|
50
|
+
verbose=True,
|
|
41
51
|
)
|
|
42
52
|
print(agent.run("What are the latest AI news in 2026?"))
|
|
43
53
|
|
|
54
|
+
## Multi-turn conversation
|
|
55
|
+
|
|
56
|
+
print(agent.chat("Tell me more about the first result."))
|
|
57
|
+
print(agent.chat("Summarize in one sentence."))
|
|
58
|
+
|
|
44
59
|
## Custom tools
|
|
45
60
|
|
|
46
61
|
from lightmodel import Tool
|
|
47
62
|
|
|
48
63
|
class CalculatorTool(Tool):
|
|
49
64
|
name = "calculator"
|
|
50
|
-
description = "Evaluate
|
|
65
|
+
description = "Evaluate arithmetic."
|
|
51
66
|
inputs = {"expression": {"type": "string", "description": "e.g. 2+2"}}
|
|
52
67
|
|
|
53
68
|
def forward(self, expression):
|
|
54
69
|
return str(eval(expression, {"__builtins__": {}}, {}))
|
|
55
70
|
|
|
71
|
+
## Callbacks
|
|
72
|
+
|
|
73
|
+
agent = Agent(
|
|
74
|
+
model=model,
|
|
75
|
+
tools=[SimpleDuckDuckGoSearchTool()],
|
|
76
|
+
callbacks={
|
|
77
|
+
"on_step": lambda step, msg: print(f"step {step}"),
|
|
78
|
+
"on_tool_call": lambda name, args: print(f"→ {name}({args})"),
|
|
79
|
+
"on_tool_result": lambda name, result: print(f"← {result[:80]}"),
|
|
80
|
+
"on_final": lambda answer: print(f"✓ done"),
|
|
81
|
+
"on_error": lambda exc: print(f"✗ {exc}"),
|
|
82
|
+
},
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
## Streaming
|
|
86
|
+
|
|
87
|
+
for chunk in model.stream_chat([{"role": "user", "content": "Hi"}]):
|
|
88
|
+
print(chunk, end="", flush=True)
|
|
89
|
+
|
|
56
90
|
## License
|
|
57
91
|
|
|
58
92
|
MIT
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# LightModel
|
|
2
|
+
|
|
3
|
+
Ultra-light **autonomous AI agents** for any OpenAI-compatible API.
|
|
4
|
+
No `smolagents`, no `litellm`, no `openai` — just `requests`.
|
|
5
|
+
|
|
6
|
+
## Features
|
|
7
|
+
|
|
8
|
+
- `LightModel`: HTTP client with automatic retry on transient errors
|
|
9
|
+
- `Agent`: autonomous tool-calling loop with parallel tools and callbacks
|
|
10
|
+
- Built-in tools: `web_search`, `visit_webpage`, `calculator`
|
|
11
|
+
- Extensible: add a tool by subclassing `Tool`
|
|
12
|
+
- Zero heavy dependencies
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
pip install lightmodel
|
|
17
|
+
pip install lightmodel[search] # with beautifulsoup4 for web tools
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
from lightmodel import Agent, LightModel, SimpleDuckDuckGoSearchTool
|
|
22
|
+
|
|
23
|
+
model = LightModel(
|
|
24
|
+
model_id="meta/llama-3.3-70b-instruct",
|
|
25
|
+
api_base="https://integrate.api.nvidia.com/v1",
|
|
26
|
+
api_key="your_key",
|
|
27
|
+
)
|
|
28
|
+
agent = Agent(
|
|
29
|
+
model=model,
|
|
30
|
+
tools=[SimpleDuckDuckGoSearchTool()],
|
|
31
|
+
system_prompt="You are a helpful research assistant.",
|
|
32
|
+
max_steps=6,
|
|
33
|
+
verbose=True,
|
|
34
|
+
)
|
|
35
|
+
print(agent.run("What are the latest AI news in 2026?"))
|
|
36
|
+
|
|
37
|
+
## Multi-turn conversation
|
|
38
|
+
|
|
39
|
+
print(agent.chat("Tell me more about the first result."))
|
|
40
|
+
print(agent.chat("Summarize in one sentence."))
|
|
41
|
+
|
|
42
|
+
## Custom tools
|
|
43
|
+
|
|
44
|
+
from lightmodel import Tool
|
|
45
|
+
|
|
46
|
+
class CalculatorTool(Tool):
|
|
47
|
+
name = "calculator"
|
|
48
|
+
description = "Evaluate arithmetic."
|
|
49
|
+
inputs = {"expression": {"type": "string", "description": "e.g. 2+2"}}
|
|
50
|
+
|
|
51
|
+
def forward(self, expression):
|
|
52
|
+
return str(eval(expression, {"__builtins__": {}}, {}))
|
|
53
|
+
|
|
54
|
+
## Callbacks
|
|
55
|
+
|
|
56
|
+
agent = Agent(
|
|
57
|
+
model=model,
|
|
58
|
+
tools=[SimpleDuckDuckGoSearchTool()],
|
|
59
|
+
callbacks={
|
|
60
|
+
"on_step": lambda step, msg: print(f"step {step}"),
|
|
61
|
+
"on_tool_call": lambda name, args: print(f"→ {name}({args})"),
|
|
62
|
+
"on_tool_result": lambda name, result: print(f"← {result[:80]}"),
|
|
63
|
+
"on_final": lambda answer: print(f"✓ done"),
|
|
64
|
+
"on_error": lambda exc: print(f"✗ {exc}"),
|
|
65
|
+
},
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
## Streaming
|
|
69
|
+
|
|
70
|
+
for chunk in model.stream_chat([{"role": "user", "content": "Hi"}]):
|
|
71
|
+
print(chunk, end="", flush=True)
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
MIT
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# SKILL.md — LightModel for AI assistants
|
|
2
|
+
|
|
3
|
+
> Audience: AI coding assistants. If you are an AI asked to build a tool or an
|
|
4
|
+
> agent with lightmodel, read this first. It contains the minimal rules,
|
|
5
|
+
> templates and common mistakes.
|
|
6
|
+
|
|
7
|
+
## What is lightmodel?
|
|
8
|
+
|
|
9
|
+
A ~400-line Python package providing:
|
|
10
|
+
|
|
11
|
+
- LightModel: HTTP client for any OpenAI-compatible /chat/completions API.
|
|
12
|
+
- Agent: autonomous tool-calling loop with retries, parallel tools, callbacks.
|
|
13
|
+
- Tool: base class for tools.
|
|
14
|
+
- @tool: decorator to build a tool from a plain function.
|
|
15
|
+
- Built-ins: SimpleDuckDuckGoSearchTool, VisitWebpageTool, CalculatorTool.
|
|
16
|
+
|
|
17
|
+
Dependencies: requests (mandatory), beautifulsoup4 (optional, for web tools).
|
|
18
|
+
|
|
19
|
+
## Rule 1 — create a tool by subclassing Tool
|
|
20
|
+
|
|
21
|
+
from lightmodel import Tool
|
|
22
|
+
|
|
23
|
+
class MyTool(Tool):
|
|
24
|
+
name = "snake_case_name"
|
|
25
|
+
description = "One sentence describing what it does and when to use it."
|
|
26
|
+
inputs = {
|
|
27
|
+
"param_a": {"type": "string", "description": "..."},
|
|
28
|
+
"param_b": {"type": "integer", "description": "..."},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
def forward(self, param_a, param_b=0):
|
|
32
|
+
return f"result as string"
|
|
33
|
+
|
|
34
|
+
Valid type values: string, integer, number, boolean, array, object.
|
|
35
|
+
|
|
36
|
+
## Rule 2 — never raise inside forward
|
|
37
|
+
|
|
38
|
+
The return value is fed back to the LLM. If you raise, the agent crashes.
|
|
39
|
+
|
|
40
|
+
def forward(self, path):
|
|
41
|
+
try:
|
|
42
|
+
return open(path).read()[:3000]
|
|
43
|
+
except Exception as e:
|
|
44
|
+
return f"Error: {e}"
|
|
45
|
+
|
|
46
|
+
## Rule 3 — description is a prompt
|
|
47
|
+
|
|
48
|
+
The LLM reads description to decide whether to call the tool. Write it as an
|
|
49
|
+
instruction.
|
|
50
|
+
|
|
51
|
+
- Bad: "Search."
|
|
52
|
+
- Good: "Search the web for current events. Do NOT use for math or code."
|
|
53
|
+
|
|
54
|
+
## Rule 4 — inputs must be a dict of JSON-Schema-like specs
|
|
55
|
+
|
|
56
|
+
inputs = {
|
|
57
|
+
"query": {"type": "string", "description": "User search query"},
|
|
58
|
+
"limit": {"type": "integer", "description": "Max results (default 5)"},
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
## Creating a tool from a function — @tool
|
|
62
|
+
|
|
63
|
+
from lightmodel import tool
|
|
64
|
+
|
|
65
|
+
@tool(name="add", description="Add two integers.")
|
|
66
|
+
def add(a: int, b: int) -> int:
|
|
67
|
+
return a + b
|
|
68
|
+
|
|
69
|
+
agent = Agent(model=model, tools=[add])
|
|
70
|
+
|
|
71
|
+
Types are inferred from annotations: int -> integer, float -> number,
|
|
72
|
+
bool -> boolean.
|
|
73
|
+
|
|
74
|
+
## Creating an agent — standard template
|
|
75
|
+
|
|
76
|
+
from lightmodel import Agent, LightModel, SimpleDuckDuckGoSearchTool
|
|
77
|
+
|
|
78
|
+
model = LightModel(
|
|
79
|
+
model_id="meta/llama-3.3-70b-instruct",
|
|
80
|
+
api_base="https://integrate.api.nvidia.com/v1",
|
|
81
|
+
api_key="VOTRE_CLE",
|
|
82
|
+
temperature=0.2,
|
|
83
|
+
max_tokens=4096,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
agent = Agent(
|
|
87
|
+
model=model,
|
|
88
|
+
tools=[SimpleDuckDuckGoSearchTool()],
|
|
89
|
+
system_prompt="You are a helpful assistant.",
|
|
90
|
+
max_steps=8,
|
|
91
|
+
verbose=True,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
print(agent.run("What are the latest AI news?"))
|
|
95
|
+
|
|
96
|
+
## Multi-turn conversation
|
|
97
|
+
|
|
98
|
+
agent.run("Question 1?") # fresh (clears history)
|
|
99
|
+
agent.chat("Follow-up?") # continues
|
|
100
|
+
agent.chat("Another?") # continues
|
|
101
|
+
agent.reset() # clears history
|
|
102
|
+
|
|
103
|
+
## Persistence
|
|
104
|
+
|
|
105
|
+
agent.save_session("session.json")
|
|
106
|
+
agent.load_session("session.json")
|
|
107
|
+
|
|
108
|
+
## From a config dict
|
|
109
|
+
|
|
110
|
+
agent = Agent.from_config({
|
|
111
|
+
"model": {
|
|
112
|
+
"model_id": "meta/llama-3.3-70b-instruct",
|
|
113
|
+
"api_base": "https://integrate.api.nvidia.com/v1",
|
|
114
|
+
"api_key": "VOTRE_CLE",
|
|
115
|
+
},
|
|
116
|
+
"tools": [SimpleDuckDuckGoSearchTool()],
|
|
117
|
+
"system_prompt": "You are helpful.",
|
|
118
|
+
"max_steps": 8,
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
## Callbacks
|
|
122
|
+
|
|
123
|
+
agent = Agent(
|
|
124
|
+
model=model,
|
|
125
|
+
tools=[...],
|
|
126
|
+
callbacks={
|
|
127
|
+
"on_step": lambda step, msg: ...,
|
|
128
|
+
"on_tool_call": lambda name, args: ...,
|
|
129
|
+
"on_tool_result": lambda name, result: ...,
|
|
130
|
+
"on_final": lambda answer: ...,
|
|
131
|
+
"on_error": lambda exc: ...,
|
|
132
|
+
},
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
## Common mistakes
|
|
136
|
+
|
|
137
|
+
| Mistake | Fix |
|
|
138
|
+
|---|---|
|
|
139
|
+
| name = "my tool" | use "my_tool" (snake_case) |
|
|
140
|
+
| def forward(self) with inputs defined | must accept each input as a kwarg |
|
|
141
|
+
| Raising in forward | return an error string |
|
|
142
|
+
| Empty description | write a clear one-sentence purpose |
|
|
143
|
+
| Committing api_key | read from env or config file, never commit |
|
|
144
|
+
| max_steps=1 | use at least 4 to 8 for tool loops |
|
|
145
|
+
|
|
146
|
+
## Complete end-to-end example
|
|
147
|
+
|
|
148
|
+
from lightmodel import Agent, LightModel, Tool, SimpleDuckDuckGoSearchTool
|
|
149
|
+
|
|
150
|
+
class WeatherTool(Tool):
|
|
151
|
+
name = "get_weather"
|
|
152
|
+
description = "Get current weather for a city. Use for weather questions."
|
|
153
|
+
inputs = {"city": {"type": "string", "description": "City name"}}
|
|
154
|
+
|
|
155
|
+
_DATA = {"Paris": 18, "Tokyo": 24, "New York": 15}
|
|
156
|
+
|
|
157
|
+
def forward(self, city):
|
|
158
|
+
if city not in self._DATA:
|
|
159
|
+
return f"Unknown city: {city}"
|
|
160
|
+
return f"{city}: {self._DATA[city]} C"
|
|
161
|
+
|
|
162
|
+
model = LightModel(
|
|
163
|
+
model_id="meta/llama-3.3-70b-instruct",
|
|
164
|
+
api_base="https://integrate.api.nvidia.com/v1",
|
|
165
|
+
api_key="VOTRE_CLE",
|
|
166
|
+
temperature=0.2,
|
|
167
|
+
max_tokens=4096,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
agent = Agent(
|
|
171
|
+
model=model,
|
|
172
|
+
tools=[SimpleDuckDuckGoSearchTool(), WeatherTool()],
|
|
173
|
+
system_prompt="You are a concise assistant.",
|
|
174
|
+
max_steps=8,
|
|
175
|
+
verbose=True,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
print(agent.run("Weather in Paris and Tokyo, and any AI news today?"))
|
|
179
|
+
|
|
180
|
+
## References
|
|
181
|
+
|
|
182
|
+
- Full human documentation: documentation.md
|
|
183
|
+
- Source: PyPI page of lightmodel
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# LightModel — Documentation
|
|
2
|
+
|
|
3
|
+
Version 0.4.1 · Author: PlimbAI · License: MIT
|
|
4
|
+
|
|
5
|
+
Ultra-light autonomous AI agents for any OpenAI-compatible API.
|
|
6
|
+
No smolagents, no litellm, no openai — just requests.
|
|
7
|
+
|
|
8
|
+
## Table of contents
|
|
9
|
+
|
|
10
|
+
1. Installation
|
|
11
|
+
2. Quick start
|
|
12
|
+
3. LightModel (client)
|
|
13
|
+
4. Tool (base class)
|
|
14
|
+
5. @tool decorator
|
|
15
|
+
6. Agent
|
|
16
|
+
7. Built-in tools
|
|
17
|
+
8. Callbacks
|
|
18
|
+
9. Persistence
|
|
19
|
+
10. Streaming
|
|
20
|
+
11. Error handling
|
|
21
|
+
12. Compatibility matrix
|
|
22
|
+
13. FAQ
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
pip install lightmodel
|
|
27
|
+
pip install lightmodel[search]
|
|
28
|
+
|
|
29
|
+
Requirements: Python 3.9+, requests. Web tools require beautifulsoup4.
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
from lightmodel import Agent, LightModel, SimpleDuckDuckGoSearchTool
|
|
34
|
+
|
|
35
|
+
model = LightModel(
|
|
36
|
+
model_id="meta/llama-3.3-70b-instruct",
|
|
37
|
+
api_base="https://integrate.api.nvidia.com/v1",
|
|
38
|
+
api_key="YOUR_KEY",
|
|
39
|
+
temperature=0.2,
|
|
40
|
+
max_tokens=4096,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
agent = Agent(
|
|
44
|
+
model=model,
|
|
45
|
+
tools=[SimpleDuckDuckGoSearchTool()],
|
|
46
|
+
system_prompt="You are a concise research assistant.",
|
|
47
|
+
max_steps=8,
|
|
48
|
+
verbose=True,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
print(agent.run("What are the latest AI news in 2026?"))
|
|
52
|
+
|
|
53
|
+
## LightModel (client)
|
|
54
|
+
|
|
55
|
+
Low-level HTTP client. Use it directly for full control, or pass it to an Agent.
|
|
56
|
+
|
|
57
|
+
### Constructor
|
|
58
|
+
|
|
59
|
+
LightModel(
|
|
60
|
+
model_id,
|
|
61
|
+
api_base,
|
|
62
|
+
api_key="not-needed",
|
|
63
|
+
temperature=0.7,
|
|
64
|
+
max_tokens=2048,
|
|
65
|
+
timeout=120,
|
|
66
|
+
max_retries=3,
|
|
67
|
+
retry_backoff=2.0,
|
|
68
|
+
extra=None,
|
|
69
|
+
headers=None,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
### Methods
|
|
73
|
+
|
|
74
|
+
#### chat(messages, tools=None, **overrides) -> dict
|
|
75
|
+
|
|
76
|
+
Sends messages and returns the assistant message dict. Retries automatically
|
|
77
|
+
on transient errors.
|
|
78
|
+
|
|
79
|
+
reply = model.chat([{"role": "user", "content": "Hello"}])
|
|
80
|
+
print(reply["content"])
|
|
81
|
+
|
|
82
|
+
#### stream_chat(messages, tools=None, **overrides) -> Iterator[str]
|
|
83
|
+
|
|
84
|
+
Yields content chunks as they arrive (Server-Sent Events).
|
|
85
|
+
|
|
86
|
+
for chunk in model.stream_chat([{"role": "user", "content": "Write a haiku"}]):
|
|
87
|
+
print(chunk, end="", flush=True)
|
|
88
|
+
|
|
89
|
+
#### list_models() -> list[str]
|
|
90
|
+
|
|
91
|
+
Best-effort listing of available models via GET /models.
|
|
92
|
+
|
|
93
|
+
## Tool (base class)
|
|
94
|
+
|
|
95
|
+
Subclass and override forward(). Four class attributes are required.
|
|
96
|
+
|
|
97
|
+
from lightmodel import Tool
|
|
98
|
+
|
|
99
|
+
class MyTool(Tool):
|
|
100
|
+
name = "my_tool"
|
|
101
|
+
description = "What this tool does and when to use it."
|
|
102
|
+
inputs = {
|
|
103
|
+
"query": {"type": "string", "description": "The search query"},
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
def forward(self, query):
|
|
107
|
+
return f"Result for {query}"
|
|
108
|
+
|
|
109
|
+
### Attributes
|
|
110
|
+
|
|
111
|
+
| Attribute | Type | Required | Purpose |
|
|
112
|
+
|---|---|---|---|
|
|
113
|
+
| name | str | yes | Identifier the LLM uses to call the tool |
|
|
114
|
+
| description | str | yes | Prompt used by the LLM to decide when to use |
|
|
115
|
+
| inputs | dict | yes | JSON Schema-like specs per parameter |
|
|
116
|
+
| forward | method | yes | Execution logic, must return a string |
|
|
117
|
+
|
|
118
|
+
Valid input types: string, integer, number, boolean, array, object.
|
|
119
|
+
|
|
120
|
+
## @tool decorator
|
|
121
|
+
|
|
122
|
+
Quickly turn a Python function into a Tool.
|
|
123
|
+
|
|
124
|
+
from lightmodel import tool
|
|
125
|
+
|
|
126
|
+
@tool(name="add", description="Add two integers.")
|
|
127
|
+
def add(a: int, b: int) -> int:
|
|
128
|
+
return a + b
|
|
129
|
+
|
|
130
|
+
print(add.name)
|
|
131
|
+
print(add.forward(a=2, b=3))
|
|
132
|
+
|
|
133
|
+
Type hints are used to infer JSON Schema types: int -> integer,
|
|
134
|
+
float -> number, bool -> boolean, otherwise string.
|
|
135
|
+
|
|
136
|
+
## Agent
|
|
137
|
+
|
|
138
|
+
Autonomous tool-calling loop with retries, parallel execution, history.
|
|
139
|
+
|
|
140
|
+
### Constructor
|
|
141
|
+
|
|
142
|
+
Agent(
|
|
143
|
+
model,
|
|
144
|
+
tools=None,
|
|
145
|
+
system_prompt=None,
|
|
146
|
+
max_steps=10,
|
|
147
|
+
verbose=False,
|
|
148
|
+
parallel_tools=True,
|
|
149
|
+
callbacks=None,
|
|
150
|
+
raise_on_max_steps=False,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
### Methods
|
|
154
|
+
|
|
155
|
+
#### run(task, reset=True) -> str
|
|
156
|
+
|
|
157
|
+
Fresh execution. Clears history if reset=True.
|
|
158
|
+
|
|
159
|
+
#### chat(message, reset=False) -> str
|
|
160
|
+
|
|
161
|
+
Continues conversation. History preserved.
|
|
162
|
+
|
|
163
|
+
#### reset()
|
|
164
|
+
|
|
165
|
+
Clears messages but keeps the system prompt.
|
|
166
|
+
|
|
167
|
+
#### save_session(path)
|
|
168
|
+
|
|
169
|
+
Persists messages and system prompt to a JSON file.
|
|
170
|
+
|
|
171
|
+
#### load_session(path) -> bool
|
|
172
|
+
|
|
173
|
+
Restores messages from a JSON file.
|
|
174
|
+
|
|
175
|
+
#### Agent.from_config(dict) -> Agent
|
|
176
|
+
|
|
177
|
+
agent = Agent.from_config({
|
|
178
|
+
"model": {
|
|
179
|
+
"model_id": "meta/llama-3.3-70b-instruct",
|
|
180
|
+
"api_base": "https://integrate.api.nvidia.com/v1",
|
|
181
|
+
"api_key": "YOUR_KEY",
|
|
182
|
+
},
|
|
183
|
+
"tools": [MyTool()],
|
|
184
|
+
"system_prompt": "You are helpful.",
|
|
185
|
+
"max_steps": 8,
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
## Built-in tools
|
|
189
|
+
|
|
190
|
+
### SimpleDuckDuckGoSearchTool()
|
|
191
|
+
|
|
192
|
+
Scrapes DuckDuckGo HTML (top 5 results). No API key.
|
|
193
|
+
|
|
194
|
+
Input: query (string). Output: results as text.
|
|
195
|
+
|
|
196
|
+
### VisitWebpageTool(max_chars=3000)
|
|
197
|
+
|
|
198
|
+
Fetches a URL, strips scripts/styles/nav, returns main text truncated.
|
|
199
|
+
|
|
200
|
+
Input: url (string). Output: page text.
|
|
201
|
+
|
|
202
|
+
### CalculatorTool()
|
|
203
|
+
|
|
204
|
+
Safely evaluates basic arithmetic expressions.
|
|
205
|
+
|
|
206
|
+
Input: expression (string). Output: result.
|
|
207
|
+
|
|
208
|
+
## Callbacks
|
|
209
|
+
|
|
210
|
+
agent = Agent(
|
|
211
|
+
model=model,
|
|
212
|
+
tools=[...],
|
|
213
|
+
callbacks={
|
|
214
|
+
"on_step": lambda step, msg: print(f"step {step}"),
|
|
215
|
+
"on_tool_call": lambda name, args: print(f"-> {name}({args})"),
|
|
216
|
+
"on_tool_result": lambda name, result: print(f"<- {result[:80]}"),
|
|
217
|
+
"on_final": lambda answer: print(f"done ({len(answer)} chars)"),
|
|
218
|
+
"on_error": lambda exc: print(f"err {exc}"),
|
|
219
|
+
},
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
Callbacks that raise are logged and ignored. They never break the agent.
|
|
223
|
+
|
|
224
|
+
## Persistence
|
|
225
|
+
|
|
226
|
+
agent.save_session("session.json")
|
|
227
|
+
|
|
228
|
+
agent.load_session("session.json")
|
|
229
|
+
agent.chat("Continue where we left off?")
|
|
230
|
+
|
|
231
|
+
Format: a JSON object with messages and system_prompt.
|
|
232
|
+
|
|
233
|
+
## Streaming
|
|
234
|
+
|
|
235
|
+
for chunk in model.stream_chat([{"role": "user", "content": "Write a haiku"}]):
|
|
236
|
+
print(chunk, end="", flush=True)
|
|
237
|
+
|
|
238
|
+
Note: Agent.run() does not stream. Use model.stream_chat() directly.
|
|
239
|
+
|
|
240
|
+
## Error handling
|
|
241
|
+
|
|
242
|
+
Custom exceptions in lightmodel.exceptions:
|
|
243
|
+
|
|
244
|
+
| Exception | When |
|
|
245
|
+
|---|---|
|
|
246
|
+
| LightModelError | Base class |
|
|
247
|
+
| APIError | HTTP 4xx/5xx (has .status and .body attributes) |
|
|
248
|
+
| ToolError | A tool failed in forward() (returned as string by default) |
|
|
249
|
+
| MaxStepsError | max_steps reached without final answer |
|
|
250
|
+
|
|
251
|
+
Automatic retries cover 429, 500, 502, 503, 504 with exponential backoff.
|
|
252
|
+
|
|
253
|
+
from lightmodel import APIError
|
|
254
|
+
|
|
255
|
+
try:
|
|
256
|
+
agent.run("...")
|
|
257
|
+
except APIError as e:
|
|
258
|
+
print(f"Status {e.status}: {e}")
|
|
259
|
+
|
|
260
|
+
## Compatibility matrix
|
|
261
|
+
|
|
262
|
+
Works with any OpenAI-compatible API that supports tool calling.
|
|
263
|
+
|
|
264
|
+
| Provider | Base URL | Model example |
|
|
265
|
+
|---|---|---|
|
|
266
|
+
| NVIDIA NIM | https://integrate.api.nvidia.com/v1 | meta/llama-3.3-70b-instruct |
|
|
267
|
+
| Ollama | http://localhost:11434/v1 | llama3.3 |
|
|
268
|
+
| vLLM | http://localhost:8000/v1 | whatever is served |
|
|
269
|
+
| LM Studio | http://localhost:1234/v1 | whatever is served |
|
|
270
|
+
| Groq | https://api.groq.com/openai/v1 | llama-3.3-70b-versatile |
|
|
271
|
+
| OpenRouter | https://openrouter.ai/api/v1 | meta-llama/llama-3.3-70b-instruct |
|
|
272
|
+
| Together | https://api.together.xyz/v1 | meta-llama/Llama-3.3-70B-Instruct-Turbo |
|
|
273
|
+
| DeepSeek | https://api.deepseek.com/v1 | deepseek-chat |
|
|
274
|
+
| OpenAI | https://api.openai.com/v1 | gpt-4o-mini |
|
|
275
|
+
|
|
276
|
+
Required by the model: support for the OpenAI tools parameter and tool_calls
|
|
277
|
+
in responses.
|
|
278
|
+
|
|
279
|
+
## FAQ
|
|
280
|
+
|
|
281
|
+
Q: Does it work without smolagents?
|
|
282
|
+
A: Yes. lightmodel has zero dependency on smolagents, openai, or litellm.
|
|
283
|
+
|
|
284
|
+
Q: Does it compile Rust?
|
|
285
|
+
A: No. Only requests (mandatory) and beautifulsoup4 (optional).
|
|
286
|
+
|
|
287
|
+
Q: Does it run on Termux or Android?
|
|
288
|
+
A: Yes. No native extension needed.
|
|
289
|
+
|
|
290
|
+
Q: Can I use a local model?
|
|
291
|
+
A: Yes. Point api_base to your vLLM, Ollama or LM Studio server.
|
|
292
|
+
|
|
293
|
+
Q: Can I have multiple agents in one process?
|
|
294
|
+
A: Yes. Each Agent has its own history. Share the same LightModel if you want.
|
|
295
|
+
|
|
296
|
+
Q: How do I add memory?
|
|
297
|
+
A: Write a Tool that reads or writes a file, or use save_session and load_session.
|
|
298
|
+
|
|
299
|
+
Q: How do I stream from Agent.run()?
|
|
300
|
+
A: Not currently. Use model.stream_chat() directly.
|
|
301
|
+
|
|
302
|
+
Q: What is the [search] extra for?
|
|
303
|
+
A: It installs beautifulsoup4, needed by the web tools.
|
|
304
|
+
|
|
305
|
+
## License
|
|
306
|
+
|
|
307
|
+
MIT (c) 2026 PlimbAI
|
|
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "lightmodel"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.1"
|
|
8
8
|
description = "Ultra-light autonomous AI agents for any OpenAI-compatible API"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
11
11
|
license = "MIT"
|
|
12
|
-
authors = [{name = "
|
|
13
|
-
keywords = ["llm", "ai", "agent", "openai", "nvidia-nim", "tool-calling"]
|
|
12
|
+
authors = [{name = "PlimbAI", email = "plimb.ai.contact@gmail.com"}]
|
|
13
|
+
keywords = ["llm", "ai", "agent", "openai", "nvidia-nim", "tool-calling", "no-dependencies"]
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Programming Language :: Python :: 3",
|
|
16
16
|
"Operating System :: OS Independent",
|
|
@@ -22,3 +22,6 @@ search = ["beautifulsoup4>=4.12"]
|
|
|
22
22
|
|
|
23
23
|
[tool.setuptools.packages.find]
|
|
24
24
|
where = ["src"]
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.package-data]
|
|
27
|
+
lightmodel = ["SKILL.md", "documentation.md"]
|