-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patheli5.py
More file actions
153 lines (133 loc) · 4.55 KB
/
Copy patheli5.py
File metadata and controls
153 lines (133 loc) · 4.55 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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env python3
import argparse
import json
import sys
import urllib.request
import urllib.error
OLLAMA_API = "http://localhost:11434"
LEVEL_CONFIG = {
"eli5": {
"label": "🧒 ELI5 (Explain Like I'm 5)",
"prompt": (
"You are explaining to a 5-year-old. Use only simple words, "
"a short relatable analogy, and keep it to 2-3 sentences. "
"No jargon whatsoever."
),
},
"hs": {
"label": "🎒 High School",
"prompt": (
"You are explaining to a high school student. Use a relatable "
"real-world example, define any technical terms you introduce, "
"and keep it to a short paragraph."
),
},
"college": {
"label": "🎓 College Level",
"prompt": (
"You are explaining to an undergraduate student. Be precise, "
"include the core mechanism and why it matters, and use "
"appropriate terminology with brief definitions."
),
},
"expert": {
"label": "🔬 Expert",
"prompt": (
"You are explaining to a domain expert. Be technical and nuanced. "
"Cover edge cases, trade-offs, and real-world implementation "
"considerations. Assume deep familiarity with the field."
),
},
}
ALL_LEVELS = ["eli5", "hs", "college", "expert"]
def get_model() -> str:
"""Return the first available Ollama model."""
try:
with urllib.request.urlopen(f"{OLLAMA_API}/api/tags", timeout=5) as resp:
data = json.loads(resp.read())
models = [m["name"] for m in data.get("models", [])]
if not models:
print("No Ollama models found. Run: ollama pull llama3.2", file=sys.stderr)
sys.exit(1)
return models[0]
except urllib.error.URLError:
print(
"Cannot reach Ollama. Is it running? Try: ollama serve",
file=sys.stderr,
)
sys.exit(1)
def explain(concept: str, level: str, model: str) -> None:
config = LEVEL_CONFIG[level]
print(f"\n{config['label']}")
print("-" * 40)
payload = json.dumps({
"model": model,
"messages": [
{"role": "system", "content": config["prompt"]},
{"role": "user", "content": f"Explain: {concept}"},
],
"stream": True,
}).encode()
req = urllib.request.Request(
f"{OLLAMA_API}/api/chat",
data=payload,
headers={"Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=60) as resp:
for line in resp:
chunk = json.loads(line.decode())
token = chunk.get("message", {}).get("content", "")
print(token, end="", flush=True)
if chunk.get("done"):
break
print()
except urllib.error.URLError as e:
print(f"\nRequest failed: {e}", file=sys.stderr)
sys.exit(1)
def run_interactive(model: str) -> None:
print(f"ELI5 Anything 🧠 (model: {model})")
print('Type a concept and press Enter. Type "quit" to exit.\n')
while True:
try:
concept = input("📝 Enter concept: ").strip()
except (EOFError, KeyboardInterrupt):
print("\nBye!")
break
if not concept:
continue
if concept.lower() in {"quit", "exit", "q"}:
print("Bye!")
break
print_concept_header(concept)
for level in ALL_LEVELS:
explain(concept, level, model)
def print_concept_header(concept: str) -> None:
print("\n" + "=" * 60)
print(f" Concept: {concept}")
print("=" * 60)
def main() -> None:
parser = argparse.ArgumentParser(
description="Explain any concept at multiple reading levels using Ollama."
)
parser.add_argument("concept", nargs="?", help="Concept to explain")
parser.add_argument(
"--level",
action="append",
choices=list(LEVEL_CONFIG.keys()),
dest="levels",
metavar="LEVEL",
help="Reading level(s): eli5, hs, college, expert (repeatable)",
)
parser.add_argument("--model", help="Ollama model to use (auto-detected if omitted)")
args = parser.parse_args()
model = args.model or get_model()
levels = args.levels or ALL_LEVELS
if args.concept:
print_concept_header(args.concept)
for level in levels:
explain(args.concept, level, model)
else:
run_interactive(model)
if __name__ == "__main__":
main()