tass 0.1.7__tar.gz → 0.1.9__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.
- {tass-0.1.7 → tass-0.1.9}/PKG-INFO +30 -2
- {tass-0.1.7 → tass-0.1.9}/README.md +29 -1
- tass-0.1.9/assets/tass.gif +0 -0
- {tass-0.1.7 → tass-0.1.9}/pyproject.toml +1 -1
- {tass-0.1.7 → tass-0.1.9}/src/app.py +57 -27
- {tass-0.1.7 → tass-0.1.9}/src/constants.py +1 -0
- {tass-0.1.7 → tass-0.1.9}/uv.lock +1 -1
- {tass-0.1.7 → tass-0.1.9}/.gitignore +0 -0
- {tass-0.1.7 → tass-0.1.9}/.python-version +0 -0
- {tass-0.1.7 → tass-0.1.9}/LICENSE +0 -0
- {tass-0.1.7 → tass-0.1.9}/src/__init__.py +0 -0
- {tass-0.1.7 → tass-0.1.9}/src/cli.py +0 -0
- {tass-0.1.7 → tass-0.1.9}/src/utils.py +0 -0
- {tass-0.1.7 → tass-0.1.9}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tass
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: A terminal assistant that allows you to ask an LLM to run commands.
|
|
5
5
|
Project-URL: Homepage, https://github.com/cetincan0/tass
|
|
6
6
|
Author: Can Cetin
|
|
@@ -13,6 +13,10 @@ Description-Content-Type: text/markdown
|
|
|
13
13
|
|
|
14
14
|
# tass
|
|
15
15
|
|
|
16
|
+
<p align="center">
|
|
17
|
+
<img src="assets/tass.gif" alt="Demo" />
|
|
18
|
+
</p>
|
|
19
|
+
|
|
16
20
|
A terminal assistant that allows you to ask an LLM to run commands.
|
|
17
21
|
|
|
18
22
|
## Warning
|
|
@@ -21,16 +25,40 @@ This tool can run commands including ones that can modify, move, or delete files
|
|
|
21
25
|
|
|
22
26
|
## Installation
|
|
23
27
|
|
|
28
|
+
### Using uv
|
|
29
|
+
|
|
24
30
|
```
|
|
25
31
|
uv tool install tass
|
|
26
32
|
```
|
|
27
33
|
|
|
34
|
+
### Using pip
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
pip install tass
|
|
38
|
+
```
|
|
39
|
+
|
|
28
40
|
You can run it with
|
|
29
41
|
|
|
30
42
|
```
|
|
31
43
|
tass
|
|
32
44
|
```
|
|
33
45
|
|
|
34
|
-
tass has only been tested with gpt-oss-120b using llama.cpp so far, but in theory any LLM with tool calling capabilities should work. By default, it will try connecting to http://localhost:8080. If you want to use another host, set the `TASS_HOST` environment variable.
|
|
46
|
+
tass has only been tested with gpt-oss-120b using llama.cpp so far, but in theory any LLM with tool calling capabilities should work. By default, it will try connecting to http://localhost:8080. If you want to use another host, set the `TASS_HOST` environment variable. At the moment there's no support for connecting tass to a non-local API, nor are there plans for it. For the time being, I plan on keeping tass completely local. There's no telemetry, no logs, just a simple REPL loop.
|
|
35
47
|
|
|
36
48
|
Once it's running, you can ask questions or give commands like "Create an empty file called test.txt" and it will propose a command to run after user confirmation.
|
|
49
|
+
|
|
50
|
+
You can enter multiline input by ending lines with a backslash (\\). The continuation prompt will appear until you enter a line without a trailing backslash.
|
|
51
|
+
|
|
52
|
+
## Upgrade
|
|
53
|
+
|
|
54
|
+
### Using uv
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
uv tool upgrade tass
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Using pip
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
pip install --upgrade tass
|
|
64
|
+
```
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# tass
|
|
2
2
|
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="assets/tass.gif" alt="Demo" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
3
7
|
A terminal assistant that allows you to ask an LLM to run commands.
|
|
4
8
|
|
|
5
9
|
## Warning
|
|
@@ -8,16 +12,40 @@ This tool can run commands including ones that can modify, move, or delete files
|
|
|
8
12
|
|
|
9
13
|
## Installation
|
|
10
14
|
|
|
15
|
+
### Using uv
|
|
16
|
+
|
|
11
17
|
```
|
|
12
18
|
uv tool install tass
|
|
13
19
|
```
|
|
14
20
|
|
|
21
|
+
### Using pip
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
pip install tass
|
|
25
|
+
```
|
|
26
|
+
|
|
15
27
|
You can run it with
|
|
16
28
|
|
|
17
29
|
```
|
|
18
30
|
tass
|
|
19
31
|
```
|
|
20
32
|
|
|
21
|
-
tass has only been tested with gpt-oss-120b using llama.cpp so far, but in theory any LLM with tool calling capabilities should work. By default, it will try connecting to http://localhost:8080. If you want to use another host, set the `TASS_HOST` environment variable.
|
|
33
|
+
tass has only been tested with gpt-oss-120b using llama.cpp so far, but in theory any LLM with tool calling capabilities should work. By default, it will try connecting to http://localhost:8080. If you want to use another host, set the `TASS_HOST` environment variable. At the moment there's no support for connecting tass to a non-local API, nor are there plans for it. For the time being, I plan on keeping tass completely local. There's no telemetry, no logs, just a simple REPL loop.
|
|
22
34
|
|
|
23
35
|
Once it's running, you can ask questions or give commands like "Create an empty file called test.txt" and it will propose a command to run after user confirmation.
|
|
36
|
+
|
|
37
|
+
You can enter multiline input by ending lines with a backslash (\\). The continuation prompt will appear until you enter a line without a trailing backslash.
|
|
38
|
+
|
|
39
|
+
## Upgrade
|
|
40
|
+
|
|
41
|
+
### Using uv
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
uv tool upgrade tass
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Using pip
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
pip install --upgrade tass
|
|
51
|
+
```
|
|
Binary file
|
|
@@ -15,9 +15,7 @@ from src.constants import (
|
|
|
15
15
|
SYSTEM_PROMPT,
|
|
16
16
|
TOOLS,
|
|
17
17
|
)
|
|
18
|
-
from src.utils import
|
|
19
|
-
is_read_only_command,
|
|
20
|
-
)
|
|
18
|
+
from src.utils import is_read_only_command
|
|
21
19
|
|
|
22
20
|
console = Console()
|
|
23
21
|
|
|
@@ -66,11 +64,14 @@ class TassApp:
|
|
|
66
64
|
|
|
67
65
|
prompt = (
|
|
68
66
|
"The conversation is becoming long and might soon go beyond the "
|
|
69
|
-
"context limit. Please provide a
|
|
70
|
-
"preserving all important details.
|
|
71
|
-
"
|
|
67
|
+
"context limit. Please provide a detailed summary of the conversation, "
|
|
68
|
+
"preserving all important details. Make sure context is not lost so that "
|
|
69
|
+
"the conversation can continue without needing to reclarify anything. "
|
|
70
|
+
"You don't have to preserve entire contents of files that have been read "
|
|
71
|
+
" or edited, they can be read again if necessary."
|
|
72
72
|
)
|
|
73
73
|
|
|
74
|
+
console.print("\n - Summarizing conversation...")
|
|
74
75
|
response = requests.post(
|
|
75
76
|
f"{self.host}/v1/chat/completions",
|
|
76
77
|
json={
|
|
@@ -84,6 +85,7 @@ class TassApp:
|
|
|
84
85
|
data = response.json()
|
|
85
86
|
summary = data["choices"][0]["message"]["content"]
|
|
86
87
|
self.messages = [self.messages[0], {"role": "assistant", "content": f"Summary of the conversation so far:\n{summary}"}]
|
|
88
|
+
console.print(" [green]Summarization completed[/green]")
|
|
87
89
|
|
|
88
90
|
def call_llm(self) -> bool:
|
|
89
91
|
response = requests.post(
|
|
@@ -102,22 +104,34 @@ class TassApp:
|
|
|
102
104
|
content = ""
|
|
103
105
|
reasoning_content = ""
|
|
104
106
|
tool_calls_map = {}
|
|
107
|
+
timings_str = ""
|
|
105
108
|
|
|
106
|
-
def generate_layout(
|
|
109
|
+
def generate_layout():
|
|
107
110
|
groups = []
|
|
108
111
|
|
|
109
112
|
if reasoning_content:
|
|
113
|
+
last_three_lines = "\n".join(reasoning_content.rstrip().split("\n")[-3:])
|
|
110
114
|
groups.append(Text(""))
|
|
111
|
-
groups.append(
|
|
115
|
+
groups.append(
|
|
116
|
+
Panel(
|
|
117
|
+
Text(
|
|
118
|
+
last_three_lines,
|
|
119
|
+
style="grey50",
|
|
120
|
+
),
|
|
121
|
+
title="Thought process",
|
|
122
|
+
title_align="left",
|
|
123
|
+
subtitle=timings_str,
|
|
124
|
+
style="grey50",
|
|
125
|
+
)
|
|
126
|
+
)
|
|
112
127
|
|
|
113
128
|
if content:
|
|
114
129
|
groups.append(Text(""))
|
|
115
|
-
groups.append(Markdown(content))
|
|
116
|
-
groups.append(Text(""))
|
|
130
|
+
groups.append(Markdown(content.rstrip()))
|
|
117
131
|
|
|
118
132
|
return Group(*groups)
|
|
119
133
|
|
|
120
|
-
with Live(generate_layout(
|
|
134
|
+
with Live(generate_layout(), refresh_per_second=10) as live:
|
|
121
135
|
for line in response.iter_lines():
|
|
122
136
|
line = line.decode("utf-8")
|
|
123
137
|
if not line.strip():
|
|
@@ -127,17 +141,29 @@ class TassApp:
|
|
|
127
141
|
continue
|
|
128
142
|
|
|
129
143
|
chunk = json.loads(line.removeprefix("data:"))
|
|
144
|
+
if all(k in chunk.get("timings", {}) for k in ["prompt_n", "prompt_per_second", "predicted_n", "predicted_per_second"]):
|
|
145
|
+
timings = chunk["timings"]
|
|
146
|
+
timings_str = (
|
|
147
|
+
f"Input: {timings['prompt_n']:,} tokens, {timings['prompt_per_second']:,.2f} tok/s | "
|
|
148
|
+
f"Output: {timings['predicted_n']:,} tokens, {timings['predicted_per_second']:,.2f} tok/s"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
if chunk["choices"][0]["finish_reason"]:
|
|
152
|
+
live.update(generate_layout())
|
|
153
|
+
|
|
130
154
|
delta = chunk["choices"][0]["delta"]
|
|
155
|
+
if not any([delta.get(key) for key in ["content", "reasoning_content", "tool_calls"]]):
|
|
156
|
+
continue
|
|
157
|
+
|
|
158
|
+
if delta.get("reasoning_content"):
|
|
159
|
+
reasoning_content += delta["reasoning_content"]
|
|
160
|
+
live.update(generate_layout())
|
|
161
|
+
|
|
131
162
|
if delta.get("content"):
|
|
132
163
|
content += delta["content"]
|
|
133
|
-
|
|
134
|
-
live.update(generate_layout(last_three_lines, content.rstrip()))
|
|
135
|
-
if delta.get("reasoning_content" ):
|
|
136
|
-
reasoning_content += delta["reasoning_content"]
|
|
137
|
-
last_three_lines = "\n".join(reasoning_content.rstrip().split("\n")[-3:])
|
|
138
|
-
live.update(generate_layout(last_three_lines, content.rstrip()))
|
|
164
|
+
live.update(generate_layout())
|
|
139
165
|
|
|
140
|
-
for tool_call_delta in delta.get("tool_calls"
|
|
166
|
+
for tool_call_delta in delta.get("tool_calls") or []:
|
|
141
167
|
index = tool_call_delta["index"]
|
|
142
168
|
if index not in tool_calls_map:
|
|
143
169
|
tool_calls_map[index] = (
|
|
@@ -164,16 +190,12 @@ class TassApp:
|
|
|
164
190
|
if function.get("arguments"):
|
|
165
191
|
tool_call["function"]["arguments"] += function["arguments"]
|
|
166
192
|
|
|
167
|
-
if chunk["choices"][0]["finish_reason"]:
|
|
168
|
-
last_three_lines = "\n".join(reasoning_content.rstrip().split("\n")[-3:])
|
|
169
|
-
live.update(generate_layout(last_three_lines, content.rstrip()))
|
|
170
|
-
|
|
171
193
|
self.messages.append(
|
|
172
194
|
{
|
|
173
195
|
"role": "assistant",
|
|
174
|
-
"content": content,
|
|
175
|
-
"reasoning_content": reasoning_content,
|
|
176
|
-
"tool_calls": list(tool_calls_map.values()),
|
|
196
|
+
"content": content.strip() or None,
|
|
197
|
+
"reasoning_content": reasoning_content.strip() or None,
|
|
198
|
+
"tool_calls": list(tool_calls_map.values()) or None,
|
|
177
199
|
}
|
|
178
200
|
)
|
|
179
201
|
|
|
@@ -362,14 +384,22 @@ class TassApp:
|
|
|
362
384
|
def run(self):
|
|
363
385
|
try:
|
|
364
386
|
self._check_llm_host()
|
|
365
|
-
console.print()
|
|
366
387
|
except KeyboardInterrupt:
|
|
367
388
|
console.print("\nBye!")
|
|
368
389
|
return
|
|
369
390
|
|
|
370
391
|
while True:
|
|
392
|
+
console.print()
|
|
371
393
|
try:
|
|
372
|
-
|
|
394
|
+
input_lines = []
|
|
395
|
+
while True:
|
|
396
|
+
input_line = console.input("> ")
|
|
397
|
+
if not input_line or input_line[-1] != "\\":
|
|
398
|
+
input_lines.append(input_line)
|
|
399
|
+
break
|
|
400
|
+
input_lines.append(input_line[:-1])
|
|
401
|
+
|
|
402
|
+
user_input = "\n".join(input_lines)
|
|
373
403
|
except KeyboardInterrupt:
|
|
374
404
|
console.print("\nBye!")
|
|
375
405
|
break
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|