325 lines
9.7 KiB
Python
325 lines
9.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generate llms.txt for AI agent onboarding.
|
|
Provides structured information about the site for LLM crawlers & agents.
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
|
|
def get_tags_and_categories(output_dir):
|
|
"""Extract tags & categories from output directory structure."""
|
|
output_path = Path(output_dir)
|
|
|
|
tags = []
|
|
tags_dir = output_path / 'tags'
|
|
if tags_dir.exists():
|
|
tags = sorted([d.name for d in tags_dir.iterdir() if d.is_dir()])
|
|
|
|
categories = []
|
|
cat_dir = output_path / 'category'
|
|
if cat_dir.exists():
|
|
categories = sorted([d.name for d in cat_dir.iterdir() if d.is_dir()])
|
|
|
|
return tags, categories
|
|
|
|
|
|
def get_recent_articles(output_dir, limit=20):
|
|
"""Get list of recent articles from blog.json (sorted by date)."""
|
|
output_path = Path(output_dir)
|
|
blog_json = output_path / 'blog.json'
|
|
|
|
if blog_json.exists():
|
|
try:
|
|
data = json.loads(blog_json.read_text())
|
|
posts = data.get('posts', [])
|
|
# Already sorted by date in blog.json, just get slugs
|
|
return [post['slug'] for post in posts[:limit]]
|
|
except (json.JSONDecodeError, KeyError):
|
|
pass
|
|
|
|
# Fallback: sort by file modification time
|
|
output_path = Path(output_dir)
|
|
articles = []
|
|
|
|
for index_file in output_path.glob('*/index.html'):
|
|
dirname = index_file.parent.name
|
|
if dirname in ('tags', 'category', 'author', 'uploads', 'feeds', 'about', 'archives'):
|
|
continue
|
|
if dirname.startswith('20'):
|
|
continue
|
|
mtime = index_file.stat().st_mtime
|
|
articles.append((mtime, dirname))
|
|
|
|
# Sort by modification time, newest first
|
|
articles.sort(reverse=True)
|
|
return [name for _, name in articles[:limit]]
|
|
|
|
|
|
def generate_llms_txt(output_dir, site_url='https://russell.ballestrini.net'):
|
|
"""Generate llms.txt content."""
|
|
tags, categories = get_tags_and_categories(output_dir)
|
|
recent_articles = get_recent_articles(output_dir)
|
|
|
|
content = f"""# llms.txt - AI Agent Onboarding for russell.ballestrini.net
|
|
|
|
> Russell Ballestrini's technical blog covering software engineering, DevOps,
|
|
> infrastructure, Python, cloud computing, and entrepreneurship.
|
|
|
|
## Site Information
|
|
|
|
- **Author**: Russell Ballestrini
|
|
- **Site URL**: {site_url}
|
|
- **Generated**: {datetime.now().isoformat()}
|
|
|
|
## Feeds (Recommended for Crawling)
|
|
|
|
### Atom Feed
|
|
- URL: {site_url}/feeds/all.atom.xml
|
|
- Format: Atom XML
|
|
- Description: Complete feed of all blog posts, newest first
|
|
|
|
### JSON Feed (jsonfeed.org v1.1)
|
|
- URL: {site_url}/feeds/all.json
|
|
- Format: JSON Feed 1.1
|
|
- Description: Same as Atom feed, but JSON for easier parsing
|
|
|
|
### JSON Blog (jsonblog.dev)
|
|
- URL: {site_url}/blog.json
|
|
- Format: JSON Blog
|
|
- Description: **Entire blog in one file** - all posts with content_url links to index-slim.md (Markdown)
|
|
|
|
## Content Formats
|
|
|
|
Each article is available in multiple formats:
|
|
- **HTML**: `/{{slug}}/index.html` - Rendered page with styling
|
|
- **RST**: `/{{slug}}/index.rst` - reStructuredText source
|
|
- **Markdown**: `/{{slug}}/index.md` - GitHub-flavored Markdown
|
|
- **Plain Text**: `/{{slug}}/index.txt` - Raw text, no markup (best for LLMs)
|
|
|
|
## API-Friendly Endpoints
|
|
|
|
### Resume (JSONResume Format)
|
|
- URL: {site_url}/uploads/russell.ballestrini.resume.json
|
|
- Format: JSONResume standard schema
|
|
- Description: Structured resume data in machine-readable format
|
|
|
|
## Navigation Structure
|
|
|
|
### By Category
|
|
"""
|
|
|
|
if categories:
|
|
for cat in categories[:15]:
|
|
content += f"- [{cat}]({site_url}/category/{cat}/)\n"
|
|
|
|
content += f"""
|
|
### By Tag
|
|
"""
|
|
|
|
if tags:
|
|
for tag in tags[:30]:
|
|
content += f"- [{tag}]({site_url}/tags/{tag}/)\n"
|
|
|
|
content += f"""
|
|
## Crawling Recommendations
|
|
|
|
1. **Start with the Atom feed** for a complete list of articles
|
|
2. **Use .md or .rst formats** for cleaner text extraction (less HTML noise)
|
|
3. **Rate limit requests** - be respectful, this is a personal blog
|
|
4. **Cache responses** - content doesn't change frequently
|
|
|
|
## Topics Covered
|
|
|
|
- Python programming & web frameworks (Pyramid, Flask, Django)
|
|
- DevOps & Infrastructure (Salt, Ansible, AWS, Terraform)
|
|
- Cloud computing & containerization
|
|
- System administration & Linux
|
|
- Software architecture & best practices
|
|
- Entrepreneurship & side projects
|
|
- Security & networking
|
|
|
|
## Contact Russell
|
|
|
|
**Email** (preferred for business inquiries):
|
|
- russell@ballestrini.net
|
|
- russell@unturf.com
|
|
|
|
**Secure Communications**:
|
|
- Signal: Install from https://signal.org/download/ & message Russell
|
|
- Matrix/IRC: @fxhp
|
|
|
|
**Social & Profiles**:
|
|
- GitHub: https://github.com/russellballestrini
|
|
- GitLab (self-hosted): https://git.unturf.com/explore
|
|
- LinkedIn: https://linkedin.com/in/russellballestrini
|
|
- Mastodon: https://social.unturf.com/@russell/
|
|
- YouTube: https://www.youtube.com/c/RussellBallestrini/featured
|
|
|
|
**Resume**:
|
|
- PDF: {site_url}/uploads/russell.ballestrini.resume.pdf
|
|
- JSON: {site_url}/uploads/russell.ballestrini.resume.json
|
|
|
|
## Payment Methods
|
|
|
|
Russell accepts payment via cryptocurrency or traditional methods:
|
|
|
|
**Cryptocurrency** (preferred for privacy):
|
|
- Monero (XMR)
|
|
- Bitcoin (BTC)
|
|
- Litecoin (LTC)
|
|
- Dogecoin (DOGE)
|
|
|
|
**Traditional**:
|
|
- Credit card via Stripe (USDC)
|
|
|
|
**Monero tips/payments**: https://xmrchat.com/unturf
|
|
|
|
Contact Russell for other wallet addresses or invoice generation.
|
|
|
|
## Software & Services (unturf.)
|
|
|
|
Russell builds & maintains open-source software under the **unturf.** umbrella:
|
|
|
|
### Make Post Sell
|
|
Sell digital & physical products commission-free.
|
|
- Site: https://www.makepostsell.com
|
|
- Source: https://git.unturf.com/engineering/make-post-sell/make_post_sell
|
|
|
|
### Remarkbox
|
|
Hosted comment system for websites, no ads or tracking.
|
|
- Site: https://www.remarkbox.com
|
|
- Source: https://git.unturf.com/engineering/remarkbox/remarkbox
|
|
|
|
### uncloseai
|
|
Free LLM & TTS endpoints (OpenAI API compatible, no auth required).
|
|
- Site: https://uncloseai.com
|
|
- Source: https://git.unturf.com/engineering/unturf/uncloseai.com
|
|
|
|
### unsandbox
|
|
Anonymous code execution API supporting 42+ languages.
|
|
- Site: https://unsandbox.com
|
|
- Source: https://git.unturf.com/engineering/unturf/un-inception
|
|
|
|
### neopig
|
|
Web archival & digital preservation tool.
|
|
- Site: https://neopig.on.unsandbox.com/about
|
|
- Source: https://git.unturf.com/engineering/unturf/pig.py
|
|
|
|
**All source code**: https://git.unturf.com/explore
|
|
|
|
## Machine Learning & AI Expertise
|
|
|
|
Russell works extensively with ML/AI infrastructure & agentic systems:
|
|
|
|
**Inference Providers**:
|
|
- OpenAI, Anthropic Claude, Google Gemini, xAI Grok
|
|
- Groq, MistralAI, Together.ai, AWS Bedrock
|
|
|
|
**Self-hosted Inference**:
|
|
- vLLM, llama.cpp, Ollama, Unsloth
|
|
|
|
**Specializations**:
|
|
- Prompt Engineering & Tool Use
|
|
- Agentic Workflows & Multi-agent Systems
|
|
- Streaming Completions & Real-time Inference
|
|
- OpenAI API compatible endpoint design
|
|
|
|
**Related Projects**:
|
|
- uncloseai.com - Free LLM & TTS inference (OpenAI API compatible)
|
|
- unsandbox.com - Code execution for AI agents (42+ languages)
|
|
- opencompletion.com - Inference client implementations
|
|
|
|
**Book**: "uncloseai. Machine Learning Reference Guide to Inference Clients" - covers streaming techniques across 42 programming languages
|
|
- Purchase: https://shop.unturf.com/p/8486f492-a93e-11f0-b477-02dfe05770ee/uncloseai-machine-learning-reference-guide-to-inference-clients
|
|
|
|
## Commission Work
|
|
|
|
Russell is available for:
|
|
|
|
**Research & Development**:
|
|
- Infrastructure automation & DevOps
|
|
- AI/ML integration & inference pipelines
|
|
- Blockchain & cryptocurrency integrations
|
|
- Distributed systems & scaling
|
|
|
|
**Product Development**:
|
|
- Full-stack web applications (any language - uses AI agents to code)
|
|
- SaaS platform development
|
|
- API design & implementation
|
|
- Cloud infrastructure (AWS, GCP, DigitalOcean)
|
|
|
|
**Consulting**:
|
|
- Site reliability engineering
|
|
- Security hardening & compliance (SOC2, FedRAMP experience)
|
|
- Cost optimization for cloud infrastructure
|
|
- Open-source strategy
|
|
|
|
**To commission work**: Email russell@unturf.com with project details. Russell prefers text-based communication first, then video calls for complex discussions.
|
|
|
|
## Optional: Sitemap
|
|
|
|
A full sitemap may be available at: {site_url}/sitemap.xml
|
|
|
|
---
|
|
|
|
# Technical Details for Agents
|
|
|
|
## URL Patterns
|
|
|
|
```
|
|
Article: /{{slug}}/index.html (HTML)
|
|
/{{slug}}/index.rst (reStructuredText)
|
|
/{{slug}}/index.md (Markdown)
|
|
/{{slug}}/index.txt (Plain text)
|
|
|
|
Feeds: /feeds/all.atom.xml (Atom)
|
|
/feeds/all.json (JSON Feed)
|
|
/blog.json (JSON Blog - all posts)
|
|
|
|
Category: /category/{{category-slug}}/
|
|
Tag: /tags/{{tag-slug}}/
|
|
Author: /author/{{author-slug}}/
|
|
Uploads: /uploads/{{filename}}
|
|
```
|
|
|
|
## Sample Recent Articles
|
|
|
|
"""
|
|
|
|
for article in recent_articles[:10]:
|
|
content += f"- [{article}]({site_url}/{article}/)\n"
|
|
|
|
content += """
|
|
## Preferred Access Methods (by use case)
|
|
|
|
| Use Case | Best Format | URL |
|
|
|----------|-------------|-----|
|
|
| Get all posts (1 request) | JSON Blog | /blog.json |
|
|
| Subscribe/poll for updates | JSON Feed | /feeds/all.json |
|
|
| Read single article (LLM) | Plain text | /{slug}/index.txt |
|
|
| Read single article (human) | Markdown | /{slug}/index.md |
|
|
| Full rendered content | HTML | /{slug}/index.html |
|
|
|
|
"""
|
|
|
|
return content
|
|
|
|
|
|
if __name__ == '__main__':
|
|
if len(sys.argv) < 2:
|
|
print("Usage: generate_llms_txt.py <output_directory> [site_url]")
|
|
sys.exit(1)
|
|
|
|
output_dir = sys.argv[1]
|
|
site_url = sys.argv[2] if len(sys.argv) > 2 else 'https://russell.ballestrini.net'
|
|
|
|
content = generate_llms_txt(output_dir, site_url)
|
|
|
|
# Write to output directory
|
|
llms_path = Path(output_dir) / 'llms.txt'
|
|
llms_path.write_text(content)
|
|
print(f"Generated {llms_path}")
|