Skip to content

Commit c04a172

Browse files
amanagrtimabbott
authored andcommitted
litellm: Add a tool to summarize a topic.
1 parent 2675715 commit c04a172

File tree

3 files changed

+168
-0
lines changed

3 files changed

+168
-0
lines changed

zulip/integrations/litellm/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Summarize topic
2+
3+
Generate a short summary of the last 100 messages in the provided topic URL.
4+
5+
### API Keys
6+
7+
For testing you need access token from
8+
https://huggingface.co/settings/tokens (or set the correct env
9+
variable with the access token if using a different model)
10+
11+
In `~/.zuliprc` add a section named `litellm` and set the api key for
12+
the model you are trying to use. For example:
13+
14+
```
15+
[litellm]
16+
HUGGINGFACE_API_KEY=YOUR_API_KEY
17+
```
18+
19+
### Setup
20+
21+
```bash
22+
$ pip install -r zulip/integrations/litellm/requirements.txt
23+
```
24+
25+
Just run `zulip/integrations/litellm/summarize-topic` to generate
26+
sample summary.
27+
28+
```bash
29+
$ zulip/integrations/litellm/summarize-topic --help
30+
usage: summarize-topic [-h] [--url URL] [--model MODEL]
31+
32+
options:
33+
-h, --help show this help message and exit
34+
--url URL The URL to fetch content from
35+
--model MODEL The model name to use for summarization
36+
```
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
zulip
2+
litellm
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import os
5+
import sys
6+
import urllib.parse
7+
from configparser import ConfigParser
8+
9+
from litellm import completion # type: ignore[import-not-found]
10+
11+
import zulip
12+
13+
if __name__ == "__main__":
14+
parser = argparse.ArgumentParser()
15+
parser.add_argument(
16+
"--url",
17+
type=str,
18+
help="The URL to fetch content from",
19+
default="https://chat.zulip.org/#narrow/stream/101-design/topic/more.20user.20indicators",
20+
)
21+
parser.add_argument(
22+
"--model",
23+
type=str,
24+
help="The model name to use for summarization",
25+
default="huggingface/meta-llama/Meta-Llama-3-8B-Instruct",
26+
)
27+
parser.add_argument(
28+
"--max-tokens",
29+
type=int,
30+
help="The maximum tokens permitted in the response",
31+
default=100,
32+
)
33+
parser.add_argument(
34+
"--max-messages",
35+
type=int,
36+
help="The maximum number of messages fetched from the server",
37+
default=100,
38+
)
39+
parser.add_argument(
40+
"--verbose",
41+
type=bool,
42+
help="Print verbose debugging output",
43+
default=False,
44+
)
45+
args = parser.parse_args()
46+
47+
config_file = zulip.get_default_config_filename()
48+
if not config_file:
49+
print("Could not find the Zulip configuration file. Please read the provided README.")
50+
sys.exit()
51+
52+
client = zulip.Client(config_file=config_file)
53+
54+
config = ConfigParser()
55+
# Make config parser case sensitive otherwise API keys will be lowercased
56+
# which is not supported by litellm.
57+
# https://docs.python.org/3/library/configparser.html#configparser.ConfigParser.optionxform
58+
config.optionxform = str # type: ignore[assignment, method-assign]
59+
60+
with open(config_file) as f:
61+
config.read_file(f, config_file)
62+
63+
# Set all the keys in `litellm` as environment variables.
64+
for key in config["litellm"]:
65+
if args.verbose:
66+
print("Setting key:", key)
67+
os.environ[key] = config["litellm"][key]
68+
69+
url = args.url
70+
model = args.model
71+
72+
base_url, narrow_hash = url.split("#")
73+
narrow_hash_terms = narrow_hash.split("/")
74+
channel = narrow_hash_terms[2].split("-")[1]
75+
topic = narrow_hash_terms[4]
76+
channel = urllib.parse.unquote(channel.replace(".", "%"))
77+
topic = urllib.parse.unquote(topic.replace(".", "%"))
78+
79+
narrow = [
80+
{"operator": "channel", "operand": channel},
81+
{"operator": "topic", "operand": topic},
82+
]
83+
84+
request = {
85+
"anchor": "newest",
86+
"num_before": args.max_messages,
87+
"num_after": 0,
88+
"narrow": narrow,
89+
# Fetch raw Markdown, not HTML
90+
"apply_markdown": False,
91+
}
92+
result = client.get_messages(request)
93+
if result["result"] == "error":
94+
print("Failed fetching message history", result)
95+
sys.exit(1)
96+
messages = result["messages"]
97+
98+
if len(messages) == 0:
99+
print("No messages in conversation to summarize")
100+
sys.exit(0)
101+
102+
formatted_messages = [
103+
{"content": f"{message['sender_full_name']}: {message['content']}", "role": "user"}
104+
for message in messages
105+
]
106+
107+
# Provide a instruction if using an `Instruct` model.
108+
if "Instruct" in model:
109+
formatted_messages.append(
110+
{
111+
"content": """
112+
Summarize the above content within 90 words.
113+
""",
114+
"role": "user",
115+
}
116+
)
117+
118+
# Send formatted messages to the LLM model for summarization
119+
response = completion(
120+
max_tokens=args.max_tokens,
121+
model=model,
122+
messages=formatted_messages,
123+
)
124+
125+
print("Summarized conversation URL:", url)
126+
print(
127+
f"Used {response['usage']['total_tokens']} tokens to summarize {len(formatted_messages)} Zulip messages."
128+
)
129+
print()
130+
print(response["choices"][0]["message"]["content"])

0 commit comments

Comments
 (0)