Skip to content

Commit e597b24

Browse files
committed
Summarizer FastAPI tool
1 parent 134d4e6 commit e597b24

File tree

8 files changed

+189
-0
lines changed

8 files changed

+189
-0
lines changed

servers/summarizer-tool/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# 📚 Local Summarizer Agent
2+
3+
This FastAPI server acts to summarize a given chunk of text.
4+
5+
It is assumed that you are running an ollama instance in an adjacent container with the default port available.
6+
7+
## 📦 Endpoints
8+
### POST /summarize/text
9+
Summarizes the given block of text
10+
11+
📥 Request
12+
13+
Body:
14+
```
15+
{
16+
'text':'Your blob of text here. It can be unlimited, but is recommended to be within the context window of the LLM you are asking for a summary from.'
17+
}
18+
```
19+
20+
📤 Response:
21+
22+
```
23+
{
24+
"status": "success",
25+
"summary": "A summary of your text."
26+
}
27+
```
28+
29+
### POST /summarize/chat
30+
Not yet implemented. Summarizes an exported Open WebUI chat JSON blob.
31+
32+
## 🧩 Environment Variables
33+
|Name|Description|Default|
34+
|---|---|---|
35+
|MODEL|The name of the model you are trying to reference. Should match the model in your ollama instance. | llama3|
36+
|MODEL_URL|The URL path to the model you are trying to access.|http://host.docker.internal:11434|
37+

servers/summarizer-tool/__init__.py

Whitespace-only changes.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
services:
2+
summarizer:
3+
container_name: summarizer
4+
image: python:3-slim
5+
ports:
6+
- 16000:8000
7+
restart: unless-stopped
8+
environment:
9+
- MODEL=llama3
10+
- MODEL_URL=http://host.docker.internal:11434
11+
extra_hosts:
12+
- "host.docker.internal:host-gateway"
13+
volumes:
14+
- .:/app
15+
entrypoint: >
16+
sh -c "
17+
apt update &&
18+
apt install -y git &&
19+
cd /app &&
20+
pip install -r ./requirements.txt &&
21+
fastapi run
22+
"

servers/summarizer-tool/main.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from fastapi import FastAPI, HTTPException
2+
from pydantic import BaseModel
3+
from .summarizers.text_summarizer import TextSummarizer
4+
5+
app = FastAPI(
6+
title="Summarizing Server",
7+
version="1.0.0",
8+
description="Leverages an LLM to summarize data",
9+
)
10+
11+
summarizers = {
12+
'TEXT':TextSummarizer()
13+
}
14+
15+
16+
class TextRequest(BaseModel):
17+
text: str
18+
19+
@app.post("/summarize/text")
20+
def summarize_text(data: TextRequest):
21+
try:
22+
result = summarizers['TEXT'].summarize(data.text)
23+
if 'content' in result:
24+
return {"status": "success", "summary":result['content']}
25+
else:
26+
raise HTTPException(status_code=500, detail=str(result['error']))
27+
except Exception as e:
28+
raise HTTPException(status_code=500, detail=str(e))
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
annotated-types==0.7.0
2+
anyio==4.9.0
3+
certifi==2025.1.31
4+
charset-normalizer==3.4.1
5+
click==8.1.8
6+
dnspython==2.7.0
7+
email_validator==2.2.0
8+
fastapi==0.115.12
9+
fastapi-cli==0.0.7
10+
h11==0.14.0
11+
httpcore==1.0.8
12+
httptools==0.6.4
13+
httpx==0.28.1
14+
idna==3.10
15+
Jinja2==3.1.6
16+
markdown-it-py==3.0.0
17+
MarkupSafe==3.0.2
18+
mdurl==0.1.2
19+
pydantic==2.11.3
20+
pydantic_core==2.33.1
21+
Pygments==2.19.1
22+
python-dotenv==1.1.0
23+
python-multipart==0.0.20
24+
PyYAML==6.0.2
25+
requests==2.32.3
26+
rich==14.0.0
27+
rich-toolkit==0.14.1
28+
shellingham==1.5.4
29+
sniffio==1.3.1
30+
starlette==0.46.2
31+
typer==0.15.2
32+
typing-inspection==0.4.0
33+
typing_extensions==4.13.2
34+
urllib3==2.4.0
35+
uvicorn==0.34.1
36+
uvloop==0.21.0
37+
watchfiles==1.0.5
38+
websockets==15.0.1

servers/summarizer-tool/summarizers/__init__.py

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from abc import ABC, abstractmethod
2+
3+
class BaseSummarizer(ABC):
4+
@abstractmethod
5+
def summarize(self, data: str) -> dict:
6+
"""Summarize data"""
7+
pass
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import requests
2+
from .base import BaseSummarizer
3+
import os
4+
5+
MODEL_URL=os.environ.get('MODEL_URL')
6+
MODEL=os.environ.get('MODEL')
7+
SUMMARIZE_PROMPT = """You are the summarizing agent in a long chain of agents.
8+
It is your job to responsibly capture the entirety of what is being described in incoming documents.
9+
You can scrap small details, but you must make sure to hit all the major points.
10+
These documents will be used in RAG down the line.
11+
12+
13+
For example, given the following text:
14+
"I've got updates on the tiny brains if\nyou are not familiar with brain\norganoids they are tiny human brains\nthat we can grow from stem cells you can\ngrow them in a literal jar if you want\nto but you can also hook them up to a\ncomputer or llm since a company called\nfinal spark decided to release brain\norganoid computation for industrial use\n"
15+
16+
You would respond with
17+
18+
"The speaker is discussing human brain stem cells being grown for industrial use."
19+
20+
Another example:
21+
hi, i'\''m isopod (formerly hornet)\n \ni'\''m a software engineer\n \ni write code, make costumes, and write music
22+
23+
You would respond with
24+
Isopod, formerly hornet, is a software engineer who makes costumes and writes music.
25+
26+
You always sanitize data. You always remove \n. You never mention yourself in your summaries. You never infer, only summarize what is presented. You never describe the text as summarized: you always just give the summary.
27+
"""
28+
29+
class TextSummarizer(BaseSummarizer):
30+
def summarize(self, data):
31+
payload = {
32+
"model":MODEL,
33+
"system": SUMMARIZE_PROMPT,
34+
"prompt":data,
35+
"stream":False,
36+
"options":{
37+
"temperature":0.5
38+
}
39+
}
40+
url = MODEL_URL + '/api/generate'
41+
result = requests.post(url=url, json=payload)
42+
if result.status_code == 200:
43+
json_data = result.json()
44+
if 'response' in json_data:
45+
return {
46+
'type': 'text',
47+
'source': url,
48+
'content': json_data['response']
49+
}
50+
print(result.content)
51+
return {
52+
'type': 'text',
53+
'source': url,
54+
'error': result.status_code
55+
}
56+
57+

0 commit comments

Comments
 (0)