Skip to content

Commit

Permalink
Merge pull request #2 from Qetesh/optimize-llm
Browse files Browse the repository at this point in the history
add llm timeout/max_workers, replace deny/allow_list, logger
  • Loading branch information
Qetesh authored Sep 21, 2024
2 parents d54442d + 2f366fe commit e775f7b
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 22 deletions.
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ This project fetches RSS subscription content from Miniflux via API and utilizes
The repository includes a template configuration file: `config.sample.yml`. Modify the `config.yml` to set up:

- **Miniflux**: Base URL and API key.
- **LLM**: Model settings, API key, and endpoint.
- **Agents**: Define each agent's prompt, whitelist/blacklist filters, and output style(`style_block` parameter controls whether the output is formatted as a code block in Markdown).
- **LLM**: Model settings, API key, and endpoint.Add timeout, max_workers parameters due to multithreading
- **Agents**: Define each agent's prompt, allow_list/deny_list filters, and output style(`style_block` parameter controls whether the output is formatted as a code block in Markdown).

Example `config.yml`:
```yaml
# INFO、DEBUG、WARN、ERROR
log_level: "INFO"

miniflux:
base_url: https://your.server.com
api_key: Miniflux API key here
Expand All @@ -41,21 +44,24 @@ llm:
base_url: http://host.docker.internal:11434/v1
api_key: ollama
model: llama3.1:latest
# timeout: 60
# max_workers: 4

agents:
summary:
title: "💡AI 摘要"
prompt: "Please summarize the content of the article under 50 words in Chinese. Do not add any additional Character、markdown language to the result text. 请用不超过50个汉字概括文章内容。结果文本中不要添加任何额外的字符、Markdown语言。"
style_block: true
blacklist:
deny_list:
- https://xxxx.net
whitelist:
allow_list:

translate:
title: "🌐AI 翻译"
prompt: "You are a highly skilled translation engine with expertise in the news media sector. Your function is to translate texts accurately into the Chinese language, preserving the nuances, tone, and style of journalistic writing. Do not add any explanations or annotations to the translated text."
style_block: false
blacklist:
whitelist:
deny_list:
allow_list:
- https://www.xxx.com/
```
Expand Down
14 changes: 10 additions & 4 deletions config.sample.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# INFO、DEBUG、WARN、ERROR
log_level: "INFO"

miniflux:
base_url: https://your.server.com
api_key: Miniflux API key here
Expand All @@ -6,19 +9,22 @@ llm:
base_url: http://host.docker.internal:11434/v1
api_key: ollama
model: llama3.1:latest
# timeout: 60
# max_workers: 4

agents:
summary:
title: "💡AI 摘要"
prompt: "Please summarize the content of the article under 50 words in Chinese. Do not add any additional Character、markdown language to the result text. 请用不超过50个汉字概括文章内容。结果文本中不要添加任何额外的字符、Markdown语言。"
style_block: true
blacklist:
deny_list:
- https://xxxx.net
whitelist:
allow_list:

translate:
title: "🌐AI 翻译"
prompt: "You are a highly skilled translation engine with expertise in the news media sector. Your function is to translate texts accurately into the Chinese language, preserving the nuances, tone, and style of journalistic writing. Do not add any explanations or annotations to the translated text."
style_block: false
blacklist:
whitelist:
deny_list:
allow_list:
- https://www.xxx.com/
43 changes: 31 additions & 12 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,53 @@
import concurrent.futures
import time
import logging
import traceback

import miniflux
from markdownify import markdownify as md
import markdown
from openai import OpenAI
from yaml import load, Loader
from yaml import safe_load

config = load(open('config.yml', encoding='utf8'), Loader=Loader)
config = safe_load(open('config.yml', encoding='utf8'))
miniflux_client = miniflux.Client(config['miniflux']['base_url'], api_key=config['miniflux']['api_key'])
llm_client = OpenAI(base_url=config['llm']['base_url'], api_key=config['llm']['api_key'])

logger = logging.getLogger(__name__)
logger.setLevel(config.get('log_level', 'INFO'))
formatter = logging.Formatter('%(asctime)s - %(filename)s - %(lineno)d - %(levelname)s - %(message)s')
console = logging.StreamHandler()
console.setFormatter(formatter)
logger.addHandler(console)

def process_entry(entry):
llm_result = ''
start_with_list = [name[1]['title'] for name in config['agents'].items()]
style_block = [name[1]['style_block'] for name in config['agents'].items()]
[start_with_list.append('<pre') for i in style_block if i]

for agent in config['agents'].items():
# Todo Compatible with whitelist/blacklist parameter, to be removed
allow_list = agent[1].get('allow_list') if agent[1].get('allow_list') is not None else agent[1].get('whitelist')
deny_list = agent[1]['deny_list'] if agent[1].get('deny_list') is not None else agent[1].get('blacklist')

messages = [
{"role": "system", "content": agent[1]['prompt']},
{"role": "user", "content": "The following is the input content:\n---\n " + md(entry['content']) }
]
# filter, if AI is not generating, and in whitelist, or not in blacklist
# filter, if AI is not generating, and in allow_list, or not in deny_list
if ((not entry['content'].startswith(tuple(start_with_list))) and
(((agent[1]['whitelist'] is not None) and (entry['feed']['site_url'] in agent[1]['whitelist'])) or
(agent[1]['blacklist'] is not None and entry['feed']['site_url'] not in agent[1]['blacklist']) or
(agent[1]['whitelist'] is None and agent[1]['blacklist'] is None))):
completion = llm_client.chat.completions.create( model=config['llm']['model'], messages= messages, timeout=15 )
(((allow_list is not None) and (entry['feed']['site_url'] in allow_list)) or
(deny_list is not None and entry['feed']['site_url'] not in deny_list) or
(allow_list is None and deny_list is None))):
completion = llm_client.chat.completions.create(
model=config['llm']['model'],
messages= messages,
timeout=config.get('llm', {}).get('timeout', 60)
)

response_content = completion.choices[0].message.content
print(time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()), agent[0], entry['feed']['feed_url'], response_content)
logger.info(f"\nagents:{agent[0]} \nfeed_title:{entry['title']} \nresult:{response_content}")

if agent[1]['style_block']:
llm_result = (llm_result + '<pre style="white-space: pre-wrap;"><code>\n'
Expand All @@ -45,16 +63,17 @@ def process_entry(entry):
while True:
entries = miniflux_client.get_entries(status=['unread'], limit=10000)
start_time = time.time()
print(time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()), 'Fetched unread entries: ' + str(len(entries['entries']))) if len(entries['entries']) > 0 else print(time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()), 'No new entries')
logger.info('Fetched unread entries: ' + str(len(entries['entries']))) if len(entries['entries']) > 0 else logger.info('No new entries')

with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
with concurrent.futures.ThreadPoolExecutor(max_workers=config.get('llm', {}).get('max_workers', 4)) as executor:
futures = [executor.submit(process_entry, i) for i in entries['entries']]
for future in concurrent.futures.as_completed(futures):
try:
data = future.result()
except Exception as e:
print('generated an exception: %s' % e)
logger.error(traceback.format_exc())
logger.error('generated an exception: %s' % e)

if len(entries['entries']) > 0 and time.time() - start_time >= 3:
print(time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()), 'Done')
logger.info('Done')
time.sleep(60)

0 comments on commit e775f7b

Please sign in to comment.