Repository navigation
Expand file tree
/
Copy pathassistant_cli.py
More file actions
68 lines (59 loc) · 2.6 KB
/
Copy pathassistant_cli.py
File metadata and controls
68 lines (59 loc) · 2.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
"""Interactive terminal assistant (CLI alternative to the FastAPI service).
python assistant_cli.py --model llama3.2:3b
Commands: /reset clear history · /stats toggle metrics · /quit
"""
from __future__ import annotations
import argparse
import asyncio
import sys
from app.config import settings
from app.ollama_client import ChatResult, OllamaClient, OllamaError
async def repl(model: str, system: str, temperature: float) -> None:
history = [{"role": "system", "content": system}]
show_stats = True
async with OllamaClient() as client:
if not await client.is_available(model):
print(f"Model '{model}' not found. Run: ollama pull {model}")
return
print(f"Local assistant on {model} (100% on-device). /reset /stats /quit")
while True:
try:
user = input("\nyou › ").strip()
except (EOFError, KeyboardInterrupt):
print()
return
if not user:
continue
if user == "/quit":
return
if user == "/reset":
history = history[:1]
print("(history cleared)")
continue
if user == "/stats":
show_stats = not show_stats
continue
history.append({"role": "user", "content": user})
print("ai › ", end="", flush=True)
try:
async for ev in client.stream_chat(model, history, options={"temperature": temperature}):
if isinstance(ev, ChatResult):
history.append({"role": "assistant", "content": ev.content})
m = ev.metrics
if show_stats:
print(f"\n [ttft {m.ttft_ms:.0f} ms · {m.decode_tps or 0:.1f} tok/s · "
f"{m.completion_tokens} tok · {m.total_latency_ms / 1000:.2f} s]")
elif not ev.is_thinking:
print(ev.text, end="", flush=True)
except OllamaError as exc:
history.pop()
print(f"\n[error] {exc}")
def main() -> None:
p = argparse.ArgumentParser(description="Local SLM assistant (terminal)")
p.add_argument("--model", default=settings.default_model)
p.add_argument("--temperature", type=float, default=0.7)
p.add_argument("--system", default="You are a concise, helpful assistant running fully offline.")
args = p.parse_args()
asyncio.run(repl(args.model, args.system, args.temperature))
if __name__ == "__main__":
sys.exit(main())