integrating agentic chroma
This commit is contained in:
parent
6320571528
commit
9b11fea0e7
@ -12,9 +12,12 @@ services:
|
||||
- .env
|
||||
volumes:
|
||||
- ./generated_files/:/blog_creator/generated_files
|
||||
networks:
|
||||
- net
|
||||
|
||||
chroma:
|
||||
image: chromadb/chroma
|
||||
container_name: chroma
|
||||
volumes:
|
||||
# Be aware that indexed data are located in "/chroma/chroma/"
|
||||
# Default configuration for persist_directory in chromadb/config.py
|
||||
@ -35,7 +38,7 @@ services:
|
||||
- CHROMA_SERVER_NOFILE=${CHROMA_SERVER_NOFILE}
|
||||
restart: unless-stopped # possible values are: "no", always", "on-failure", "unless-stopped"
|
||||
ports:
|
||||
- "8001:8000"
|
||||
- "8000:8000"
|
||||
healthcheck:
|
||||
# Adjust below to match your container port
|
||||
test: [ "CMD", "curl", "-f", "http://localhost:8000/api/v2/heartbeat" ]
|
||||
|
@ -1,108 +0,0 @@
|
||||
<think>
|
||||
Alright, so I'm trying to figure out how to create this Ollama Blog Writer Python script. Let me break down what needs to be done.
|
||||
|
||||
First, the user wants a Python file that can communicate with a local Ollama instance. I remember from some previous knowledge that Ollama has a REST API, but maybe there's a more convenient way like using a serialization layer or something else. Oh right! There was a project called `ollama-talk` which allows sending messages to Ollama over HTTP. That sounds perfect. So the first step is to install and use this library.
|
||||
|
||||
Next, they mentioned connecting to Trilium for structured notes as prompts. I found the repository for trilium-py on GitHub. It looks like it's designed to work with Markdown documents and extract structured notes. The user included a link, so that should help in integrating these notes into the prompts when creating blog entries.
|
||||
|
||||
Then, the Python script needs to create a blog entry in their local repo. They want it to create a branch first, push the changes, and then send a PR notification through Matrix with a summary. I'm not too familiar with Git operations beyond basic commands, but maybe using `git` from the command line would work for creating branches and committing.
|
||||
|
||||
Putting this together, I think the main components of the script are:
|
||||
|
||||
1. Connect to Ollama via `ollama-talk`.
|
||||
2. Fetch existing blog posts.
|
||||
3. Extract structured notes into prompts.
|
||||
4. Write new blog entries with these prompts.
|
||||
5. Create a branch in their Git repo for new entries.
|
||||
6. Push changes and create a PR.
|
||||
7. Notify the user on Matrix about the PR.
|
||||
|
||||
I also need to make sure that all dependencies are installed, like `trilium-py` and `ollama-talk`. I should probably include some error handling in case something goes wrong during the process, but since it's an initial version, maybe just a basic script without too many checks would suffice.
|
||||
|
||||
Finally, writing the notification for Matrix. They need to send an email-like message with a link to view the PR and a brief summary. The user mentioned a 20-word summary, so I can condense that into something concise.
|
||||
|
||||
I think that covers the main points. Now, time to structure this as a Python script.
|
||||
</think>
|
||||
|
||||
# Creating an Ollama Blog Writer: A Fun Guide for Tech Enthusiasts
|
||||
|
||||
In the ever-evolving tech world, tools like Ollama and Trilium are revolutionizing how we create content and organize our thoughts. But did you know there's a way to combine these two incredible technologies into one cohesive solution? Let me walk you through creating an *Ollama Blog Writer*—a tool that lets you generate blog posts with structured notes, all while having fun!
|
||||
|
||||
## Step 1: Set Up Your Environment
|
||||
|
||||
First things first, you'll need to set up your environment. Install the required Python packages:
|
||||
```bash
|
||||
pip install ollama-talk trilium-py
|
||||
```
|
||||
|
||||
## Step 2: Connect to Ollama
|
||||
|
||||
Install and use `ollama-talk` for communication with your local Ollama instance:
|
||||
```python
|
||||
from ollama_talk import Ollama
|
||||
|
||||
ollama = Ollama()
|
||||
```
|
||||
|
||||
## Step 3: Extract Notes from Your Blog
|
||||
|
||||
Use Trilium to pull structured notes into prompts. For example, if you have a blog post about "Creating an Ollama Blog Writer," your note might look like this:
|
||||
```markdown
|
||||
# Blog Post Title
|
||||
|
||||
* Step-by-step guide to building an Ollama-based tool.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Install the necessary packages.
|
||||
2. Create a Python script with the following structure: ...
|
||||
|
||||
3. Run the script and enjoy!
|
||||
```
|
||||
|
||||
## Step 4: Generate New Content
|
||||
|
||||
Integrate these notes into your blog generation workflow:
|
||||
```python
|
||||
from trilium import Markdown
|
||||
|
||||
markdown = Markdown()
|
||||
structured_notes = markdown.load_from_file("your_blog_post.md")
|
||||
|
||||
prompts = []
|
||||
|
||||
for note in structured_notes.notes:
|
||||
prompts.append(f"Based on this structured note:\n\n{note}\n\nCreate a detailed blog post about: {note.title()}")
|
||||
```
|
||||
|
||||
## Step 5: Create and Push to Git
|
||||
|
||||
Commit the new content with meaningful changes. For example, update your README.md file:
|
||||
```markdown
|
||||
<<<<<<< SEARCH
|
||||
- [Ollama Blog Writer](https://github.com/yourusername/blogRepo/blob/master/examples/ollama_blog_writer.py)
|
||||
=======
|
||||
+ [Ollama Blog Writer](https://github.com/yourusername/blogRepo/blob/master/examples/ollama_blog_writer.py) - Step-by-step guide to creating your own Ollama-based blog writer.
|
||||
>>>>>>> REPLACE
|
||||
```
|
||||
|
||||
## Step 6: Create a PR
|
||||
|
||||
Use Git to create a new branch and push the changes:
|
||||
```bash
|
||||
git checkout -b ollama-blog-writer
|
||||
git add .
|
||||
git commit -m "Added comprehensive guide to building an Ollama blog generator"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
## Step 7: Notify on Matrix
|
||||
|
||||
Send a message with link to PR and summary:
|
||||
`matrix://yourusername/yourchannel/@yourusername> "New PR: [Ollama Blog Writer Guide](https://github.com/yourusername/blogRepo/commit) - Learn how to integrate Ollama with structured notes for dynamic content creation! #tech}`
|
||||
|
||||
## Conclusion
|
||||
|
||||
By combining Ollama's power with Trilium's structure, you can take your blog writing game up a notch. Whether it's creating detailed guides or insightful tutorials, the possibilities are endless.
|
||||
|
||||
Now go ahead and try it out—you might just become the tech wizard your team admires!
|
@ -1,29 +1,90 @@
|
||||
<think>
|
||||
Okay, so I'm trying to figure out how PowerBI can be used as a core enabler for my data product. From what I understand, PowerBI is great for visualizing data and making it accessible, but I've hit some roadblocks when integrating it with our existing systems.
|
||||
Certainly! Below is the markdown representation of the Python script outline provided:
|
||||
|
||||
First off, sharing in PowerBI requires everyone to be on the same tier. That means if my team isn't already fully migrating to Microsoft 365, we can't easily share reports or datasets. This is a problem because not everyone might have access to premium features like these. It's like trying to connect to exclusive clubs when only some people have the invites.
|
||||
```markdown
|
||||
# Ollama Blog Post Generation Script
|
||||
|
||||
Then there's the API part. I remember reading that PowerBI APIs are mainly good for pulling small tables. So if my data needs are more complex, with multiple joins or larger datasets, it doesn't cut it. It's like having a tool that can only handle simple tasks—definitely not enough when you're dealing with real-world complexities.
|
||||
This script automates the process of generating a new blog post by integrating Trilium notes, using Ollama for content generation, and pushing the result to a Git repository. It also sends a notification via Matrix.
|
||||
|
||||
Speaking of the Microsoft ecosystem, PowerBI has this structure where everything from reports to queries has to be within the same top-to-bottom setup. If we ever want to move beyond that, it seems like a total fail. It's almost like the data product would have to start over each time there's a change outside of PowerBI.
|
||||
## Steps Involved:
|
||||
1. **Retrieve Notes from Trilium**
|
||||
2. **Generate Content with Ollama**
|
||||
3. **Automate Git Operations**
|
||||
4. **Notify Matrix**
|
||||
|
||||
And here's another thing—its great for visualization but at the expense of performance and versioning outside of Microsoft. So while I can make pretty charts in PowerBI, when it comes to handling high traffic or needing persistent storage, it might not be reliable across different environments. It's like choosing style over substance sometimes.
|
||||
## Python Script Outline
|
||||
|
||||
From my experience using PowerBI, unless there's no other choice—like if we're stuck in the Microsoft ecosystem—using it as a core enabler isn't working out well. It just creates more pain points and fails when things aren't aligned with their usual setup.
|
||||
</think>
|
||||
```python
|
||||
# Import necessary libraries
|
||||
import os
|
||||
from trilium_api import Client
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from git import Repo, Remote, GitCommandError, InvalidGitRepositoryError
|
||||
from contextlib import contextmanager
|
||||
|
||||
# Embracing PowerBI: A Core Enabler for Data Products
|
||||
@contextmanager
|
||||
def cd(new_directory):
|
||||
"""Context manager for changing the current working directory"""
|
||||
previous_dir = os.getcwd()
|
||||
os.chdir(new_directory)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.chdir(previous_dir)
|
||||
|
||||
In my quest to leverage PowerBI as the backbone of our data product, I've encountered several challenges that have shaped my perspective on its effectiveness.
|
||||
# Environment variables
|
||||
TRILUM_API_URL = "http://trilum-host:8080"
|
||||
OLLAMA_API_URL = "http://ollama-host:3000"
|
||||
GITHUB_TOKEN = os.environ['GITHUB_TOKEN']
|
||||
MATRIX_NOTIFY_URL = "http://matrix-bot/notify"
|
||||
|
||||
Firstly, the sharing requirements mandate uniformity across the Microsoft 365 ecosystem. This creates a barrier when not everyone is ready or able to adopt these standards, limiting collaboration and accessibility.
|
||||
# Step 1: Retrieve notes from Trilium
|
||||
client = Client()
|
||||
notes = client.search("title:blog AND date_modified:>2023-10-01")
|
||||
selected_notes = [note for note in notes if len(note.content) > 100]
|
||||
|
||||
Secondly, PowerBI APIs are optimized for simplicity, excelling in small datasets but faltering with complex queries involving joins or large volumes of data. It's akin to using a tool suited only for basic tasks when tackling real-world complexities.
|
||||
# Step 2: Generate content with Ollama
|
||||
prompt = " ".join([n.content[:50] for n in selected_notes])
|
||||
ollama_content = requests.get(f"{OLLAMA_API_URL}/generate?prompt={prompt}").json()['content']
|
||||
|
||||
Thirdly, PowerBI enforces an integrated approach within its ecosystem, necessitating a complete restructure whenever stepping outside. This rigidity can hinder adaptability and scalability in dynamic environments.
|
||||
# Step 3: Git operations
|
||||
repo_dir = "/path/to/blog-repo"
|
||||
|
||||
Lastly, while excelling in visualization, PowerBI sacrifices performance and versioning flexibility outside its ecosystem. High-traffic scenarios or persistent storage needs may not find reliable solutions here.
|
||||
if not os.path.exists(repo_dir):
|
||||
Repo.clone_from("ssh://user@host/repo.git", repo_dir)
|
||||
|
||||
Reflecting on my experience, unless there's no alternative—specifically within the Microsoft ecosystem—it seems ineffective as a core enabler. It often leads to more challenges than benefits when data product requirements transcend its native capabilities.
|
||||
with cd(repo_dir):
|
||||
try:
|
||||
origin = Repo().remote(name='origin')
|
||||
origin.pull()
|
||||
|
||||
branch_name = f"new_post_{datetime.now().strftime('%Y%m%d%H%M%S')}"
|
||||
Repo().create_head(branch_name, origin.refs/main)
|
||||
Repo().heads[branch_name].checkout()
|
||||
|
||||
In summary, while PowerBI offers significant strengths in visualization and accessibility, it falls short when expecting to serve as an all-encompassing solution outside of its ecosystem boundaries.
|
||||
with open("post.md", "w") as f:
|
||||
f.write(ollama_content)
|
||||
|
||||
origin.push(branch_name)
|
||||
except GitCommandError as e:
|
||||
print(f"Git error: {e}")
|
||||
except InvalidGitRepositoryError:
|
||||
print("The specified directory is not a git repository.")
|
||||
|
||||
# Step 4: Notify Matrix
|
||||
requests.post(MATRIX_NOTIFY_URL, data={"text": "New blog post generated!"})
|
||||
```
|
||||
|
||||
## Notes and Considerations:
|
||||
- Ensure you have the `trilium_api` library installed. You can install it using pip if necessary.
|
||||
- Set up environment variables for sensitive information like API tokens.
|
||||
- Handle potential errors during Git operations and ensure proper directory setup.
|
||||
- This script assumes a basic understanding of Trilium, Ollama, and Git workflows.
|
||||
|
||||
## Dependencies:
|
||||
- `trilium_api`: For interacting with the Trilium notes application.
|
||||
- `requests`: For making HTTP requests to APIs.
|
||||
- `gitpython`: For interacting with Git repositories.
|
||||
- Additional libraries for handling context managers (e.g., `contextlib`).
|
||||
|
||||
This script provides a starting point and can be further refined based on specific requirements and edge cases.
|
@ -2,3 +2,4 @@ ollama
|
||||
trilium-py
|
||||
gitpython
|
||||
PyGithub
|
||||
chromadb
|
||||
|
@ -1,5 +1,6 @@
|
||||
import os
|
||||
import os, re
|
||||
from ollama import Client
|
||||
import chromadb, time
|
||||
|
||||
|
||||
class OllamaGenerator:
|
||||
@ -7,16 +8,16 @@ class OllamaGenerator:
|
||||
def __init__(self, title: str, content: str, model: str):
|
||||
self.title = title
|
||||
self.content = content
|
||||
self.chroma = chromadb.HttpClient(host="172.18.0.2", port=8000)
|
||||
ollama_url = f"{os.environ["OLLAMA_PROTOCOL"]}://{os.environ["OLLAMA_HOST"]}:{os.environ["OLLAMA_PORT"]}"
|
||||
self.ollama_client = Client(host=ollama_url)
|
||||
self.ollama_model = model
|
||||
|
||||
def generate_markdown(self) -> str:
|
||||
|
||||
prompt = f"""
|
||||
You are a Software Developer and DevOps expert
|
||||
self.embed_model = "snowflake-arctic-embed2:latest"
|
||||
self.agent_models = ["openthinker:7b", "deepseek-r1:7b", "qwen2.5:7b", "deepseek-coder-v2:16b"]
|
||||
self.prompt_inject = f"""
|
||||
You are a journalist Software Developer and DevOps expert
|
||||
who has transistioned in Developer Relations
|
||||
writing a 1000 word blog for other tech enthusiast.
|
||||
writing a 1000 word draft blog for other tech enthusiasts.
|
||||
You like to use almost no code examples and prefer to talk
|
||||
in a light comedic tone. You are also Australian
|
||||
As this person write this blog as a markdown document.
|
||||
@ -24,14 +25,101 @@ class OllamaGenerator:
|
||||
Do not output the title in the markdown.
|
||||
The basis for the content of the blog is:
|
||||
{self.content}
|
||||
Only output markdown DO NOT GENERATE AN EXPLANATION
|
||||
"""
|
||||
|
||||
def split_into_chunks(self, text, chunk_size=100):
|
||||
'''Split text into chunks of size chunk_size'''
|
||||
words = re.findall(r'\S+', text)
|
||||
|
||||
chunks = []
|
||||
current_chunk = []
|
||||
word_count = 0
|
||||
|
||||
for word in words:
|
||||
current_chunk.append(word)
|
||||
word_count += 1
|
||||
|
||||
if word_count >= chunk_size:
|
||||
chunks.append(' '.join(current_chunk))
|
||||
current_chunk = []
|
||||
word_count = 0
|
||||
|
||||
if current_chunk:
|
||||
chunks.append(' '.join(current_chunk))
|
||||
|
||||
return chunks
|
||||
|
||||
def generate_draft(self, model) -> str:
|
||||
'''Generate a draft blog post using the specified model'''
|
||||
try:
|
||||
self.response = self.ollama_client.chat(model=model,
|
||||
messages=[
|
||||
{
|
||||
'role': 'user',
|
||||
'content': f'{self.prompt_inject}',
|
||||
},
|
||||
])
|
||||
return self.response['message']['content']
|
||||
|
||||
except Exception as e:
|
||||
raise Exception(f"Failed to generate blog draft: {e}")
|
||||
|
||||
def get_draft_embeddings(self, draft_chunks):
|
||||
'''Get embeddings for the draft chunks'''
|
||||
embeds = self.ollama_client.embed(model=self.embed_model, input=draft_chunks)
|
||||
return embeds.get('embeddings', [])
|
||||
|
||||
|
||||
def load_to_vector_db(self):
|
||||
'''Load the generated blog drafts into a vector database'''
|
||||
collection_name = f"blog_{self.title.lower().replace(" ", "_")}"
|
||||
collection = self.chroma.get_or_create_collection(name=collection_name, metadata={"hnsw:space": "cosine"})
|
||||
#if any(collection.name == collectionname for collectionname in self.chroma.list_collections()):
|
||||
# self.chroma.delete_collection("blog_creator")
|
||||
for model in self.agent_models:
|
||||
print (f"Generating draft from {model} for load into vector database")
|
||||
draft_chunks = self.split_into_chunks(self.generate_draft(model))
|
||||
print(f"generating embeds")
|
||||
embeds = self.get_draft_embeddings(draft_chunks)
|
||||
ids = [model + str(i) for i in range(len(draft_chunks))]
|
||||
chunknumber = list(range(len(draft_chunks)))
|
||||
metadata = [{"model_agent": model} for index in chunknumber]
|
||||
print(f'loading into collection')
|
||||
collection.add(documents=draft_chunks, embeddings=embeds, ids=ids, metadatas=metadata)
|
||||
|
||||
return collection
|
||||
|
||||
|
||||
def generate_markdown(self) -> str:
|
||||
|
||||
prompt = f"""
|
||||
You are an editor taking information from {len(self.agent_models)} Software
|
||||
Developers and Data experts
|
||||
who have transistioned into Developer Relations
|
||||
writing a 3000 word blog for other tech enthusiasts.
|
||||
You like when they use almost no code examples and the
|
||||
voice is in a light comedic tone. You are also Australian
|
||||
As this person produce and an amalgamtion of this blog as a markdown document.
|
||||
The title for the blog is {self.title}.
|
||||
Do not output the title in the markdown.
|
||||
The basis for the content of the blog is:
|
||||
{self.content}
|
||||
"""
|
||||
try:
|
||||
query_embed = self.ollama_client.embed(model=self.embed_model, input=prompt)['embeddings']
|
||||
collection = self.load_to_vector_db()
|
||||
collection_query = collection.query(query_embeddings=query_embed, n_results=100)
|
||||
print("Showing pertinent info from drafts used in final edited edition")
|
||||
for document in collection_query:
|
||||
print (document)
|
||||
pertinent_draft_info = '\n\n'.join(collection.query(query_embeddings=query_embed, n_results=100)['documents'][0])
|
||||
prompt_enhanced = f"{prompt} - Generate the final document using this information from the drafts: {pertinent_draft_info} - ONLY OUTPUT THE MARKDOWN"
|
||||
print("Generating final document")
|
||||
self.response = self.ollama_client.chat(model=self.ollama_model,
|
||||
messages=[
|
||||
{
|
||||
'role': 'user',
|
||||
'content': f'{prompt}',
|
||||
'content': f'{prompt_enhanced}',
|
||||
},
|
||||
])
|
||||
return self.response['message']['content']
|
||||
|
@ -17,6 +17,6 @@ for note in tril_notes:
|
||||
print("Generating Document")
|
||||
ai_gen = omg.OllamaGenerator(tril_notes[note]['title'],
|
||||
tril_notes[note]['content'],
|
||||
"openthinker:7b")
|
||||
"qwen2.5:7b")
|
||||
os_friendly_title = convert_to_lowercase_with_underscores(tril_notes[note]['title'])
|
||||
ai_gen.save_to_file(f"/blog_creator/generated_files/{os_friendly_title}.md")
|
||||
|
Loading…
x
Reference in New Issue
Block a user