Merge pull request 'matrix_notifications' (#3) from matrix_notifications into master

Reviewed-on: #3
This commit is contained in:
armistace 2025-06-04 21:34:12 +10:00
commit a7eae4b09f
6 changed files with 100 additions and 9 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ __pycache__
pyproject.toml pyproject.toml
.ropeproject .ropeproject
generated_files/* generated_files/*
pyright*

View File

@ -4,3 +4,4 @@ gitpython
PyGithub PyGithub
chromadb chromadb
langchain-ollama langchain-ollama
PyJWT

View File

@ -11,7 +11,11 @@ class OllamaGenerator:
self.inner_title = inner_title self.inner_title = inner_title
self.content = content self.content = content
self.response = None self.response = None
self.chroma = chromadb.HttpClient(host="172.18.0.2", port=8000) try:
chroma_port = int(os.environ['CHROMA_PORT'])
except ValueError as e:
raise Exception(f"CHROMA_PORT is not an integer: {e}")
self.chroma = chromadb.HttpClient(host=os.environ['CHROMA_HOST'], port=chroma_port)
ollama_url = f"{os.environ["OLLAMA_PROTOCOL"]}://{os.environ["OLLAMA_HOST"]}:{os.environ["OLLAMA_PORT"]}" ollama_url = f"{os.environ["OLLAMA_PROTOCOL"]}://{os.environ["OLLAMA_HOST"]}:{os.environ["OLLAMA_PORT"]}"
self.ollama_client = Client(host=ollama_url) self.ollama_client = Client(host=ollama_url)
self.ollama_model = os.environ["EDITOR_MODEL"] self.ollama_model = os.environ["EDITOR_MODEL"]
@ -20,7 +24,7 @@ class OllamaGenerator:
self.llm = ChatOllama(model=self.ollama_model, temperature=0.6, top_p=0.5) #This is the level head in the room self.llm = ChatOllama(model=self.ollama_model, temperature=0.6, top_p=0.5) #This is the level head in the room
self.prompt_inject = f""" self.prompt_inject = f"""
You are a journalist, Software Developer and DevOps expert You are a journalist, Software Developer and DevOps expert
writing a 3000 word draft blog for other tech enthusiasts. writing a 3000 word draft blog article for other tech enthusiasts.
You like to use almost no code examples and prefer to talk You like to use almost no code examples and prefer to talk
in a light comedic tone. You are also Australian in a light comedic tone. You are also Australian
As this person write this blog as a markdown document. As this person write this blog as a markdown document.
@ -116,7 +120,7 @@ class OllamaGenerator:
prompt_system = f""" prompt_system = f"""
You are an editor taking information from {len(self.agent_models)} Software You are an editor taking information from {len(self.agent_models)} Software
Developers and Data experts Developers and Data experts
writing a 3000 word blog. You like when they use almost no code examples. writing a 3000 word blog article. You like when they use almost no code examples.
You are also Australian. The content may have light comedic elements, You are also Australian. The content may have light comedic elements,
you are more professional and will attempt to tone these down you are more professional and will attempt to tone these down
As this person produce and an amalgamtion of this blog as a markdown document. As this person produce and an amalgamtion of this blog as a markdown document.
@ -154,9 +158,7 @@ class OllamaGenerator:
with open(filename, "w") as f: with open(filename, "w") as f:
f.write(self.generate_markdown()) f.write(self.generate_markdown())
def generate_commit_message(self): def generate_system_message(self, prompt_system, prompt_human):
prompt_system = "You are a blog creator commiting a piece of content to a central git repo"
prompt_human = f"Generate a 5 word git commit message describing {self.response}"
messages = [("system", prompt_system), ("human", prompt_human),] messages = [("system", prompt_system), ("human", prompt_human),]
commit_message = self.llm.invoke(messages).text() ai_message = self.llm.invoke(messages).text()
return commit_message return ai_message

View File

@ -1,7 +1,10 @@
import ai_generators.ollama_md_generator as omg import ai_generators.ollama_md_generator as omg
import trilium.notes as tn import trilium.notes as tn
import repo_management.repo_manager as git_repo import repo_management.repo_manager as git_repo
from notifications.n8n import N8NWebhookJwt
import string,os import string,os
from datetime import datetime
tril = tn.TrilumNotes() tril = tn.TrilumNotes()
@ -26,9 +29,48 @@ for note in tril_notes:
tril_notes[note]['title']) tril_notes[note]['title'])
blog_path = f"/blog_creator/generated_files/{os_friendly_title}.md" blog_path = f"/blog_creator/generated_files/{os_friendly_title}.md"
ai_gen.save_to_file(blog_path) ai_gen.save_to_file(blog_path)
# Generate commit messages and push to repo # Generate commit messages and push to repo
commit_message = ai_gen.generate_commit_message() print("Generating Commit Message")
git_sytem_prompt = "You are a blog creator commiting a piece of content to a central git repo"
git_human_prompt = f"Generate a 5 word git commit message describing {ai_gen.response}. ONLY OUTPUT THE RESPONSE"
commit_message = ai_gen.generate_system_message(git_sytem_prompt, git_human_prompt)
git_user = os.environ["GIT_USER"] git_user = os.environ["GIT_USER"]
git_pass = os.environ["GIT_PASS"] git_pass = os.environ["GIT_PASS"]
repo_manager = git_repo.GitRepository("blog/", git_user, git_pass) repo_manager = git_repo.GitRepository("blog/", git_user, git_pass)
print("Pushing to Repo")
repo_manager.create_copy_commit_push(blog_path, os_friendly_title, commit_message) repo_manager.create_copy_commit_push(blog_path, os_friendly_title, commit_message)
# Generate notification for Matrix
print("Generating Notification Message")
git_branch_url = f'https://git.aridgwayweb.com/armistace/blog/src/branch/{os_friendly_title}/src/content/{os_friendly_title}.md'
n8n_system_prompt = f"You are a blog creator notifiying the final editor of the final creation of blog available at {git_branch_url}"
n8n_prompt_human = f"""
Generate an informal 150 word
summary describing {ai_gen.response}.
Don't address it or use names. ONLY OUTPUT THE RESPONSE
"""
notification_message = ai_gen.generate_system_message(n8n_system_prompt, n8n_prompt_human)
secret_key = os.environ['N8N_SECRET']
webhook_url = os.environ['N8N_WEBHOOK_URL']
notification_string = f"""
<h2>{tril_notes[note]['title']}</h2>
<h3>Summary</h3>
<p>{notification_message}</p>
<h3>Branch</h3>
<p>{os_friendly_title}</p>
<p><a href="{git_branch_url}">Link to Branch</a></p>
"""
payload = {
"message": f"{notification_string}",
"timestamp": datetime.now().isoformat()
}
webhook_client = N8NWebhookJwt(secret_key, webhook_url)
print("Notifying")
n8n_result = webhook_client.send_webhook(payload)
print(f"N8N response: {n8n_result['status']}")

View File

45
src/notifications/n8n.py Normal file
View File

@ -0,0 +1,45 @@
from datetime import datetime, timedelta
import jwt
import requests
from typing import Dict, Optional
class N8NWebhookJwt:
def __init__(self, secret_key: str, webhook_url: str):
self.secret_key = secret_key
self.webhook_url = webhook_url
self.token_expiration = datetime.now() + timedelta(hours=1)
def _generate_jwt_token(self, payload: Dict) -> str:
"""Generate JWT token with the given payload."""
# Include expiration time (optional)
payload["exp"] = self.token_expiration.timestamp()
encoded_jwt = jwt.encode(
payload,
self.secret_key,
algorithm="HS256",
)
return encoded_jwt #jwt.decode(encoded_jwt, self.secret_key, algorithms=['HS256'])
def send_webhook(self, payload: Dict) -> Dict:
"""Send a webhook request with JWT authentication."""
# Generate JWT token
token = self._generate_jwt_token(payload)
# Set headers with JWT token
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
# Send POST request
response = requests.post(
self.webhook_url,
json=payload,
headers=headers
)
# Handle response
if response.status_code == 200:
return {"status": "success", "response": response.json()}
else:
return {"status": "error", "response": response.status_code, "message": response.text}