sallm-agent 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.
- sallm_agent-0.1.0/.gitignore +17 -0
- sallm_agent-0.1.0/PKG-INFO +155 -0
- sallm_agent-0.1.0/README.md +138 -0
- sallm_agent-0.1.0/pyproject.toml +45 -0
- sallm_agent-0.1.0/src/sallm/__init__.py +38 -0
- sallm_agent-0.1.0/src/sallm/agent.py +412 -0
- sallm_agent-0.1.0/src/sallm/cli/__init__.py +6 -0
- sallm_agent-0.1.0/src/sallm/cli/__main__.py +4 -0
- sallm_agent-0.1.0/src/sallm/cli/chat.py +357 -0
- sallm_agent-0.1.0/src/sallm/cli/display.py +242 -0
- sallm_agent-0.1.0/src/sallm/cli/optimize_cmd.py +117 -0
- sallm_agent-0.1.0/src/sallm/context.py +128 -0
- sallm_agent-0.1.0/src/sallm/control.py +172 -0
- sallm_agent-0.1.0/src/sallm/extract_queue.py +165 -0
- sallm_agent-0.1.0/src/sallm/legacy_ask.py +164 -0
- sallm_agent-0.1.0/src/sallm/llm.py +44 -0
- sallm_agent-0.1.0/src/sallm/memory/__init__.py +52 -0
- sallm_agent-0.1.0/src/sallm/memory/chunking.py +56 -0
- sallm_agent-0.1.0/src/sallm/memory/config.py +85 -0
- sallm_agent-0.1.0/src/sallm/memory/embedding.py +44 -0
- sallm_agent-0.1.0/src/sallm/memory/gates.py +48 -0
- sallm_agent-0.1.0/src/sallm/memory/index.py +131 -0
- sallm_agent-0.1.0/src/sallm/memory/lance.py +204 -0
- sallm_agent-0.1.0/src/sallm/memory/query.py +104 -0
- sallm_agent-0.1.0/src/sallm/memory/retrieval.py +28 -0
- sallm_agent-0.1.0/src/sallm/memory/types.py +69 -0
- sallm_agent-0.1.0/src/sallm/messages.py +14 -0
- sallm_agent-0.1.0/src/sallm/metrics.py +46 -0
- sallm_agent-0.1.0/src/sallm/models.py +79 -0
- sallm_agent-0.1.0/src/sallm/optimization/__init__.py +21 -0
- sallm_agent-0.1.0/src/sallm/optimization/artifacts.py +58 -0
- sallm_agent-0.1.0/src/sallm/optimization/dataset.py +43 -0
- sallm_agent-0.1.0/src/sallm/optimization/metrics.py +42 -0
- sallm_agent-0.1.0/src/sallm/optimization/search.py +154 -0
- sallm_agent-0.1.0/src/sallm/profiles/gemma4-e4b-v1.json +22 -0
- sallm_agent-0.1.0/src/sallm/prom.py +430 -0
- sallm_agent-0.1.0/src/sallm/prompt.py +177 -0
- sallm_agent-0.1.0/src/sallm/receipt.py +176 -0
- sallm_agent-0.1.0/src/sallm/skills.py +70 -0
- sallm_agent-0.1.0/src/sallm/state/__init__.py +17 -0
- sallm_agent-0.1.0/src/sallm/state/models.py +98 -0
- sallm_agent-0.1.0/src/sallm/state/repository.py +370 -0
- sallm_agent-0.1.0/src/sallm/tools/__init__.py +109 -0
- sallm_agent-0.1.0/src/sallm/tools/calc.py +152 -0
- sallm_agent-0.1.0/src/sallm/tools/dig.py +86 -0
- sallm_agent-0.1.0/src/sallm/tools/echo.py +35 -0
- sallm_agent-0.1.0/src/sallm/tools/runner.py +229 -0
- sallm_agent-0.1.0/src/sallm/trace.py +550 -0
- sallm_agent-0.1.0/src/sallm/turn.py +230 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sallm-agent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Minimal tool-calling agent over LiteLLM, tuned for local Ollama models
|
|
5
|
+
Project-URL: Homepage, https://github.com/codref/sallm-agent
|
|
6
|
+
Project-URL: Repository, https://github.com/codref/sallm-agent
|
|
7
|
+
Project-URL: Issues, https://github.com/codref/sallm-agent/issues
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Requires-Dist: lancedb
|
|
10
|
+
Requires-Dist: litellm
|
|
11
|
+
Requires-Dist: peewee
|
|
12
|
+
Requires-Dist: rich
|
|
13
|
+
Requires-Dist: typer
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# sallm-agent
|
|
19
|
+
|
|
20
|
+
Minimal tool-calling agent (`sallm`) for **local small LLMs** (default: Gemma 4 4B via Ollama).
|
|
21
|
+
|
|
22
|
+
**Focus:** keep context predictable over long sessions — durable SQLite state, LanceDB retrieval, a skill stack, and a visible `ContextReceipt` that explains token spend.
|
|
23
|
+
|
|
24
|
+
Not an invisible RAG black box: raw messages stay canonical; vectors are a rebuildable index; derived facts must cite source message ids.
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
ollama pull gemma4:e4b-it-qat
|
|
30
|
+
ollama pull qwen3-embedding:0.6b
|
|
31
|
+
|
|
32
|
+
uv sync --extra dev
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Core deps include `peewee` (SQLite ORM) and `lancedb` (vector index). There is **no** DSPy/Pydantic dependency.
|
|
36
|
+
|
|
37
|
+
## Turn pipeline
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
user → persist raw message
|
|
41
|
+
→ goal/skill control (small JSON call)
|
|
42
|
+
→ vector retrieve (Qwen embed + LanceDB)
|
|
43
|
+
→ budgeted prompt + ReAct ```run tools
|
|
44
|
+
→ persist answer
|
|
45
|
+
→ extract grounded facts + index chunks
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Library
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from sallm import Agent, RetrievalConfig, Skill, SkillRegistry
|
|
52
|
+
from sallm.tools import builtin_tools
|
|
53
|
+
|
|
54
|
+
agent = Agent(
|
|
55
|
+
tools=builtin_tools(("calc", "echo")),
|
|
56
|
+
state_path="/tmp/sallm/state.db",
|
|
57
|
+
vector_path="/tmp/sallm/vectors",
|
|
58
|
+
session_id="demo",
|
|
59
|
+
retrieval=RetrievalConfig(
|
|
60
|
+
memory_gate=True,
|
|
61
|
+
search_mode="dense", # or "hybrid"
|
|
62
|
+
use_instruct=True,
|
|
63
|
+
use_rewrite=False,
|
|
64
|
+
use_hyde=False,
|
|
65
|
+
),
|
|
66
|
+
)
|
|
67
|
+
result = agent.ask("Remember the code is PURPLE-42.")
|
|
68
|
+
print(result["answer"])
|
|
69
|
+
print(result["receipt"]) # ContextReceipt as dict
|
|
70
|
+
print(result["goal"], result["stack"])
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Resume by reusing `state_path` + `session_id`.
|
|
74
|
+
|
|
75
|
+
### VectorStore contract
|
|
76
|
+
|
|
77
|
+
Implement `upsert` / `search` / `delete_session` / `close` (see `sallm.memory.types.VectorStore`). Default: `LanceVectorStore`. A future **pgvector** adapter can satisfy the same dataclasses (`VectorRecord`, `VectorQuery`, `VectorHit`) without changing the agent.
|
|
78
|
+
|
|
79
|
+
SQLite stores chunk text + `indexed` flags; LanceDB is rebuilt from those rows after a crash.
|
|
80
|
+
|
|
81
|
+
### Skills
|
|
82
|
+
|
|
83
|
+
Default skill is `converse`. Register more with `SkillRegistry` (name, description, prompt fragment, optional tool subset).
|
|
84
|
+
|
|
85
|
+
### Compiled profiles
|
|
86
|
+
|
|
87
|
+
Neutral JSON under `sallm/profiles/` (instructions + demos + budgets). Offline:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
uv run sallm optimize --dataset data/cases.jsonl --task controller --out /tmp/profile.json
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`sallm chat` never optimizes at startup; it only loads a profile.
|
|
94
|
+
|
|
95
|
+
## CLI
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Durable long session (recommended)
|
|
99
|
+
uv run sallm chat \
|
|
100
|
+
--state-path .sallm/state.db \
|
|
101
|
+
--vector-path .sallm/vectors \
|
|
102
|
+
--session long1 \
|
|
103
|
+
--retrieval-query instruct \
|
|
104
|
+
--search dense \
|
|
105
|
+
--memory-gate \
|
|
106
|
+
--extract waterfall \
|
|
107
|
+
--tools echo,calc
|
|
108
|
+
|
|
109
|
+
uv run sallm chat --show-prompt
|
|
110
|
+
uv run sallm chat --script tests/fixtures/sample_conversation.txt
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Slash commands: `/help`, `/clear`, `/history`, `/prompt`, `/state`, `/stack`, `/memory`, `/context`, `/quit`.
|
|
114
|
+
|
|
115
|
+
### Tool contract
|
|
116
|
+
|
|
117
|
+
| Rule | Detail |
|
|
118
|
+
|------|--------|
|
|
119
|
+
| Identity | Tool name = first argv token |
|
|
120
|
+
| Help | Every tool supports `--help` |
|
|
121
|
+
| Args | CLI flags only (no JSON blobs) |
|
|
122
|
+
| Intermediate | stdout may start with `[intermediate]` |
|
|
123
|
+
|
|
124
|
+
````markdown
|
|
125
|
+
```run
|
|
126
|
+
calc --expression "2**10"
|
|
127
|
+
```
|
|
128
|
+
````
|
|
129
|
+
|
|
130
|
+
Shipped tools: `echo`, `calc`, `dig`.
|
|
131
|
+
|
|
132
|
+
### Legacy context optimizers
|
|
133
|
+
|
|
134
|
+
Still available without durable state: `--context max-messages|summarize`. Prefer `--state-path` + retrieval for hour-scale sessions.
|
|
135
|
+
|
|
136
|
+
## Tests
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
uv run pytest tests/ -v
|
|
140
|
+
# E2E needs Ollama + gemma4:e4b-it-qat (+ qwen3-embedding:0.6b for stack memory)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Limits
|
|
144
|
+
|
|
145
|
+
Retrieval improves grounding; it does **not** guarantee the model never invents facts. Source-tagged memory and `ContextReceipt` make misses inspectable.
|
|
146
|
+
|
|
147
|
+
## How it works
|
|
148
|
+
|
|
149
|
+
Walkthrough with an example, stage-by-stage flow, token-budget simulation, and hypothesis checks: [docs/how-the-agent-works.md](docs/how-the-agent-works.md).
|
|
150
|
+
|
|
151
|
+
When a script turn (briefing / transcript) is larger than the history budget: [docs/oversized-briefings.md](docs/oversized-briefings.md).
|
|
152
|
+
|
|
153
|
+
Offline prompt/parameter tuning: [docs/optimize-prompts.md](docs/optimize-prompts.md).
|
|
154
|
+
|
|
155
|
+
Skills (selection, stack, tools): [docs/skills.md](docs/skills.md).
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# sallm-agent
|
|
2
|
+
|
|
3
|
+
Minimal tool-calling agent (`sallm`) for **local small LLMs** (default: Gemma 4 4B via Ollama).
|
|
4
|
+
|
|
5
|
+
**Focus:** keep context predictable over long sessions — durable SQLite state, LanceDB retrieval, a skill stack, and a visible `ContextReceipt` that explains token spend.
|
|
6
|
+
|
|
7
|
+
Not an invisible RAG black box: raw messages stay canonical; vectors are a rebuildable index; derived facts must cite source message ids.
|
|
8
|
+
|
|
9
|
+
## Setup
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
ollama pull gemma4:e4b-it-qat
|
|
13
|
+
ollama pull qwen3-embedding:0.6b
|
|
14
|
+
|
|
15
|
+
uv sync --extra dev
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Core deps include `peewee` (SQLite ORM) and `lancedb` (vector index). There is **no** DSPy/Pydantic dependency.
|
|
19
|
+
|
|
20
|
+
## Turn pipeline
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
user → persist raw message
|
|
24
|
+
→ goal/skill control (small JSON call)
|
|
25
|
+
→ vector retrieve (Qwen embed + LanceDB)
|
|
26
|
+
→ budgeted prompt + ReAct ```run tools
|
|
27
|
+
→ persist answer
|
|
28
|
+
→ extract grounded facts + index chunks
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Library
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from sallm import Agent, RetrievalConfig, Skill, SkillRegistry
|
|
35
|
+
from sallm.tools import builtin_tools
|
|
36
|
+
|
|
37
|
+
agent = Agent(
|
|
38
|
+
tools=builtin_tools(("calc", "echo")),
|
|
39
|
+
state_path="/tmp/sallm/state.db",
|
|
40
|
+
vector_path="/tmp/sallm/vectors",
|
|
41
|
+
session_id="demo",
|
|
42
|
+
retrieval=RetrievalConfig(
|
|
43
|
+
memory_gate=True,
|
|
44
|
+
search_mode="dense", # or "hybrid"
|
|
45
|
+
use_instruct=True,
|
|
46
|
+
use_rewrite=False,
|
|
47
|
+
use_hyde=False,
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
result = agent.ask("Remember the code is PURPLE-42.")
|
|
51
|
+
print(result["answer"])
|
|
52
|
+
print(result["receipt"]) # ContextReceipt as dict
|
|
53
|
+
print(result["goal"], result["stack"])
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Resume by reusing `state_path` + `session_id`.
|
|
57
|
+
|
|
58
|
+
### VectorStore contract
|
|
59
|
+
|
|
60
|
+
Implement `upsert` / `search` / `delete_session` / `close` (see `sallm.memory.types.VectorStore`). Default: `LanceVectorStore`. A future **pgvector** adapter can satisfy the same dataclasses (`VectorRecord`, `VectorQuery`, `VectorHit`) without changing the agent.
|
|
61
|
+
|
|
62
|
+
SQLite stores chunk text + `indexed` flags; LanceDB is rebuilt from those rows after a crash.
|
|
63
|
+
|
|
64
|
+
### Skills
|
|
65
|
+
|
|
66
|
+
Default skill is `converse`. Register more with `SkillRegistry` (name, description, prompt fragment, optional tool subset).
|
|
67
|
+
|
|
68
|
+
### Compiled profiles
|
|
69
|
+
|
|
70
|
+
Neutral JSON under `sallm/profiles/` (instructions + demos + budgets). Offline:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
uv run sallm optimize --dataset data/cases.jsonl --task controller --out /tmp/profile.json
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`sallm chat` never optimizes at startup; it only loads a profile.
|
|
77
|
+
|
|
78
|
+
## CLI
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Durable long session (recommended)
|
|
82
|
+
uv run sallm chat \
|
|
83
|
+
--state-path .sallm/state.db \
|
|
84
|
+
--vector-path .sallm/vectors \
|
|
85
|
+
--session long1 \
|
|
86
|
+
--retrieval-query instruct \
|
|
87
|
+
--search dense \
|
|
88
|
+
--memory-gate \
|
|
89
|
+
--extract waterfall \
|
|
90
|
+
--tools echo,calc
|
|
91
|
+
|
|
92
|
+
uv run sallm chat --show-prompt
|
|
93
|
+
uv run sallm chat --script tests/fixtures/sample_conversation.txt
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Slash commands: `/help`, `/clear`, `/history`, `/prompt`, `/state`, `/stack`, `/memory`, `/context`, `/quit`.
|
|
97
|
+
|
|
98
|
+
### Tool contract
|
|
99
|
+
|
|
100
|
+
| Rule | Detail |
|
|
101
|
+
|------|--------|
|
|
102
|
+
| Identity | Tool name = first argv token |
|
|
103
|
+
| Help | Every tool supports `--help` |
|
|
104
|
+
| Args | CLI flags only (no JSON blobs) |
|
|
105
|
+
| Intermediate | stdout may start with `[intermediate]` |
|
|
106
|
+
|
|
107
|
+
````markdown
|
|
108
|
+
```run
|
|
109
|
+
calc --expression "2**10"
|
|
110
|
+
```
|
|
111
|
+
````
|
|
112
|
+
|
|
113
|
+
Shipped tools: `echo`, `calc`, `dig`.
|
|
114
|
+
|
|
115
|
+
### Legacy context optimizers
|
|
116
|
+
|
|
117
|
+
Still available without durable state: `--context max-messages|summarize`. Prefer `--state-path` + retrieval for hour-scale sessions.
|
|
118
|
+
|
|
119
|
+
## Tests
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
uv run pytest tests/ -v
|
|
123
|
+
# E2E needs Ollama + gemma4:e4b-it-qat (+ qwen3-embedding:0.6b for stack memory)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Limits
|
|
127
|
+
|
|
128
|
+
Retrieval improves grounding; it does **not** guarantee the model never invents facts. Source-tagged memory and `ContextReceipt` make misses inspectable.
|
|
129
|
+
|
|
130
|
+
## How it works
|
|
131
|
+
|
|
132
|
+
Walkthrough with an example, stage-by-stage flow, token-budget simulation, and hypothesis checks: [docs/how-the-agent-works.md](docs/how-the-agent-works.md).
|
|
133
|
+
|
|
134
|
+
When a script turn (briefing / transcript) is larger than the history budget: [docs/oversized-briefings.md](docs/oversized-briefings.md).
|
|
135
|
+
|
|
136
|
+
Offline prompt/parameter tuning: [docs/optimize-prompts.md](docs/optimize-prompts.md).
|
|
137
|
+
|
|
138
|
+
Skills (selection, stack, tools): [docs/skills.md](docs/skills.md).
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sallm-agent"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Minimal tool-calling agent over LiteLLM, tuned for local Ollama models"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"litellm",
|
|
9
|
+
"typer",
|
|
10
|
+
"rich",
|
|
11
|
+
"peewee",
|
|
12
|
+
"lancedb",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.urls]
|
|
16
|
+
Homepage = "https://github.com/codref/sallm-agent"
|
|
17
|
+
Repository = "https://github.com/codref/sallm-agent"
|
|
18
|
+
Issues = "https://github.com/codref/sallm-agent/issues"
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
sallm = "sallm.cli.chat:app"
|
|
22
|
+
sallm-echo = "sallm.tools.echo:main"
|
|
23
|
+
sallm-calc = "sallm.tools.calc:main"
|
|
24
|
+
sallm-dig = "sallm.tools.dig:main"
|
|
25
|
+
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
dev = ["pytest"]
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["hatchling"]
|
|
31
|
+
build-backend = "hatchling.build"
|
|
32
|
+
|
|
33
|
+
[tool.hatch.build.targets.sdist]
|
|
34
|
+
include = [
|
|
35
|
+
"src/sallm",
|
|
36
|
+
"README.md",
|
|
37
|
+
"pyproject.toml",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[tool.hatch.build.targets.wheel]
|
|
41
|
+
packages = ["src/sallm"]
|
|
42
|
+
|
|
43
|
+
# Map src/sallm -> sallm in the wheel
|
|
44
|
+
[tool.hatch.build.targets.wheel.sources]
|
|
45
|
+
"src/sallm" = "sallm"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Public package exports — agent + prompt + memory/state types."""
|
|
2
|
+
|
|
3
|
+
from .agent import Agent
|
|
4
|
+
from .memory import (
|
|
5
|
+
LanceVectorStore,
|
|
6
|
+
RetrievalConfig,
|
|
7
|
+
VectorHit,
|
|
8
|
+
VectorQuery,
|
|
9
|
+
VectorRecord,
|
|
10
|
+
VectorStore,
|
|
11
|
+
)
|
|
12
|
+
from .messages import DEFAULT_API_BASE, DEFAULT_MODEL
|
|
13
|
+
from .models import EmbeddingProfile, ModelProfile
|
|
14
|
+
from .prompt import CompiledProfile, Prompt
|
|
15
|
+
from .receipt import ContextReceipt
|
|
16
|
+
from .skills import Skill, SkillRegistry
|
|
17
|
+
from .tools import CliTool, ToolResult
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"Agent",
|
|
21
|
+
"CliTool",
|
|
22
|
+
"CompiledProfile",
|
|
23
|
+
"ContextReceipt",
|
|
24
|
+
"EmbeddingProfile",
|
|
25
|
+
"LanceVectorStore",
|
|
26
|
+
"ModelProfile",
|
|
27
|
+
"Prompt",
|
|
28
|
+
"RetrievalConfig",
|
|
29
|
+
"Skill",
|
|
30
|
+
"SkillRegistry",
|
|
31
|
+
"ToolResult",
|
|
32
|
+
"VectorHit",
|
|
33
|
+
"VectorQuery",
|
|
34
|
+
"VectorRecord",
|
|
35
|
+
"VectorStore",
|
|
36
|
+
"DEFAULT_MODEL",
|
|
37
|
+
"DEFAULT_API_BASE",
|
|
38
|
+
]
|