Skip to content

Instantly share code, notes, and snippets.

@Manojbhat09
Created August 12, 2025 06:32
Show Gist options
  • Save Manojbhat09/ac6cad00d3b1c86755bcccf905db2859 to your computer and use it in GitHub Desktop.
Save Manojbhat09/ac6cad00d3b1c86755bcccf905db2859 to your computer and use it in GitHub Desktop.
Ollama-based Grug Translator: CLI implementation using DSPy with Ollama for local LLM execution

Ollama Grug Translator

Local LLM implementation of the Grug speak translator using Ollama and DSPy framework.

Overview

This project demonstrates how to build a translation system that converts plain English text into "Grug speak" (caveman-style language) using DSPy with Ollama for local model execution. No external API keys required.

Files

  • ollama_grug_translator.py - CLI script that reads JSON input and outputs JSON results
  • utils_ollama_grug.py - Utility functions using only Ollama
  • README_OLLAMA.md - This file
  • requirements_ollama.txt - Python dependencies

Setup

1. Install Ollama

First, install Ollama on your system:

Linux/macOS:

curl -fsSL https://ollama.com/install.sh | sh

Windows: Download from https://ollama.com/download

2. Pull a Model

Pull the default model (llama3.2) or another compatible model:

ollama pull llama3.2

Other supported models:

ollama pull llama3.1
ollama pull mistral
ollama pull codellama

3. Start Ollama Service

Ensure Ollama service is running:

ollama serve

4. Install Python Dependencies

pip install -r requirements_ollama.txt

Usage

Basic Usage (stdin)

echo '{"text": "You should not construct complex systems."}' | python ollama_grug_translator.py

Output:

{"status": "success", "result": "grug no make big complicated thing. big thing bad. make grug brain hurt."}

Using Input File

python ollama_grug_translator.py --input-json input.json

Where input.json contains:

{"text": "Modern software development practices are essential."}

Custom Model

python ollama_grug_translator.py --model mistral --input-json input.json

Custom Ollama URL

python ollama_grug_translator.py --base-url http://192.168.1.100:11434 --input-json input.json

JSON Examples

Successful Translation

Input:

{"text": "Object-oriented programming is a programming paradigm."}

Output:

{
  "status": "success",
  "result": "grug make thing like thing. thing have properties. grug group similar things together. make code less confusing for grug brain."
}

Error Handling

Input (missing text field):

{"message": "hello world"}

Output:

{
  "status": "error",
  "error": "Missing 'text' field in input"
}

Input (invalid text type):

{"text": 123}

Output:

{
  "status": "error",
  "error": "'text' field must be a string"
}

Model Connection Error

Output when Ollama is not running:

{
  "status": "error",
  "error": "Connection to Ollama failed. Ensure Ollama is running and the model is available."
}

Architecture

Key Components

  1. CLI Interface (ollama_grug_translator.py)

    • Reads JSON from file or stdin
    • Configures DSPy with Ollama
    • Outputs structured JSON responses
    • Never prints non-JSON content
  2. Utility Functions (utils_ollama_grug.py)

    • translate_grug() - Core translation using DSPy + Ollama
    • automated_readability_index() - Text complexity measurement
    • similarity_metric() - Embedding-based similarity using Ollama
    • overall_metric() - Combined quality assessment
    • retry() - Robust error handling wrapper
  3. Error Handling

    • Exponential backoff retry mechanism
    • Graceful degradation for embeddings
    • Always outputs valid JSON

Troubleshooting

Common Issues

  1. "Connection refused" errors

    • Ensure Ollama service is running: ollama serve
    • Check if port 11434 is available
    • Verify firewall settings
  2. "Model not found" errors

    • Pull the required model: ollama pull llama3.2
    • Check available models: ollama list
  3. Slow responses

    • Consider using smaller models (e.g., llama3.2 instead of llama3.1)
    • Ensure adequate RAM (8GB+ recommended)
    • Check CPU/GPU utilization
  4. JSON parsing errors

    • Ensure input is valid JSON
    • Check that 'text' field is present and is a string

Performance Optimization

  • Use GPU acceleration if available
  • Consider quantized models for faster inference
  • Adjust max_tokens parameter in utils for longer/shorter outputs
  • Use smaller models for development/testing

Model Compatibility

Tested with:

  • ✅ llama3.2 (recommended)
  • ✅ llama3.1
  • ✅ mistral
  • ✅ codellama
  • ⚠️ Larger models may require more RAM

Safety Notes

  • All processing is done locally - no data sent to external services
  • Models run in isolated Ollama environment
  • No persistent storage of sensitive data
  • Suitable for offline/air-gapped environments

Example Workflow

# 1. Start Ollama
ollama serve &

# 2. Ensure model is available
ollama pull llama3.2

# 3. Test the translator
echo '{"text": "Complex software architecture requires careful planning."}' | \
    python ollama_grug_translator.py

# Expected output:
# {"status": "success", "result": "grug make big software thing need think hard first. plan good before grug start make thing."}

License

Educational example demonstrating local LLM integration with DSPy.

#!/usr/bin/env python3
"""
Ollama Grug Translator: CLI that reads --input-json (or stdin) and prints a single JSON object
with fields {status, result or error}. Uses only Ollama via dspy.Ollama and emits no non-JSON.
"""
import argparse
import json
import sys
from typing import Dict, Any
import dspy
from utils_ollama_grug import translate_grug, retry
def parse_arguments():
"""Parse command line arguments"""
parser = argparse.ArgumentParser(description='Ollama Grug Translator CLI')
parser.add_argument('--input-json', type=str, help='JSON file path to read input from')
parser.add_argument('--model', type=str, default='llama3.2', help='Ollama model to use (default: llama3.2)')
parser.add_argument('--base-url', type=str, default='http://localhost:11434', help='Ollama base URL (default: http://localhost:11434)')
return parser.parse_args()
def read_input(input_json_path: str = None) -> Dict[str, Any]:
"""Read input from file or stdin"""
try:
if input_json_path:
with open(input_json_path, 'r') as f:
return json.load(f)
else:
return json.load(sys.stdin)
except Exception as e:
raise ValueError(f"Failed to read input: {str(e)}")
def setup_ollama(model: str, base_url: str) -> None:
"""Setup DSPy with Ollama"""
ollama_lm = dspy.Ollama(model=model, base_url=base_url, max_tokens=1000)
dspy.settings.configure(lm=ollama_lm)
def process_translation(input_data: Dict[str, Any]) -> Dict[str, Any]:
"""Process translation request"""
if 'text' not in input_data:
return {"status": "error", "error": "Missing 'text' field in input"}
text = input_data['text']
if not isinstance(text, str):
return {"status": "error", "error": "'text' field must be a string"}
try:
# Use retry wrapper for robust translation
result = retry(lambda: translate_grug(text), max_retries=3)
return {"status": "success", "result": result}
except Exception as e:
return {"status": "error", "error": str(e)}
def main():
"""Main function"""
try:
args = parse_arguments()
# Setup Ollama
setup_ollama(args.model, args.base_url)
# Read input
input_data = read_input(args.input_json)
# Process translation
output = process_translation(input_data)
# Output JSON only
print(json.dumps(output))
except Exception as e:
# Ensure we always output valid JSON
error_output = {"status": "error", "error": str(e)}
print(json.dumps(error_output))
sys.exit(1)
if __name__ == "__main__":
main()
dspy-ai
requests
beautifulsoup4
python-dotenv
ollama
#!/usr/bin/env python3
"""
Utility functions for Ollama Grug Translator
Ollama-only implementation with translate_grug(), automated_readability_index,
similarity_metric using Ollama embeddings, overall_metric, and retry.
"""
import dspy
import re
import time
import ollama
from typing import List, Dict, Any, Callable
from functools import wraps
def retry(func: Callable, max_retries: int = 3, delay: float = 1.0) -> Any:
"""Retry wrapper for functions that may fail"""
@wraps(func)
def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
if attempt < max_retries - 1:
time.sleep(delay * (2 ** attempt)) # Exponential backoff
continue
raise last_exception
return wrapper()
# DSPy Signature for Grug Translation
class GrugTranslation(dspy.Signature):
"""Translate plain english to Grug text."""
plain_english = dspy.InputField()
grug_text = dspy.OutputField()
# DSPy Module for Chain of Thought
class CoT(dspy.Module):
def __init__(self):
super().__init__()
self.prog = dspy.ChainOfThought(GrugTranslation)
def forward(self, plain_english):
return self.prog(plain_english=plain_english)
def translate_grug(text: str) -> str:
"""Translate plain English to Grug speak using Ollama"""
translator = CoT()
result = translator.forward(text)
return result.grug_text
def automated_readability_index(text: str) -> float:
"""Calculate Automated Readability Index (ARI)"""
# Count characters (ignoring whitespace)
characters = len(re.sub(r'\s+', '', text))
# Count words by splitting the text
words = len(text.split())
# Count sentences by finding period, exclamation, question mark, or newlines
sentences = len(re.findall(r'[.!?\n]', text))
if words == 0 or sentences == 0: # Prevent division by zero
return 0
# Calculate the Automated Readability Index (ARI)
ari = (4.71 * (characters / words)) + (0.5 * (words / sentences)) - 21.43
return round(ari, 2)
def get_ollama_embedding(text: str, model: str = "llama3.2") -> List[float]:
"""Get embedding from Ollama"""
try:
response = ollama.embeddings(model=model, prompt=text)
return response['embedding']
except Exception as e:
# Fallback to a simple numerical representation if embeddings fail
return [float(hash(text) % 1000) / 1000.0] * 768 # Standard embedding size
def cosine_similarity(vec1: List[float], vec2: List[float]) -> float:
"""Calculate cosine similarity between two vectors"""
if len(vec1) != len(vec2):
return 0.0
dot_product = sum(a * b for a, b in zip(vec1, vec2))
magnitude1 = sum(a * a for a in vec1) ** 0.5
magnitude2 = sum(b * b for b in vec2) ** 0.5
if magnitude1 == 0 or magnitude2 == 0:
return 0.0
return dot_product / (magnitude1 * magnitude2)
def similarity_metric(truth: Any, pred: Any, trace: Any = None, model: str = "llama3.2") -> bool:
"""Calculate similarity metric using Ollama embeddings"""
try:
truth_text = getattr(truth, 'grug_text', str(truth))
pred_text = getattr(pred, 'grug_text', str(pred))
# Get embeddings
truth_embedding = get_ollama_embedding(truth_text, model)
pred_embedding = get_ollama_embedding(pred_text, model)
# Calculate similarity
similarity = cosine_similarity(truth_embedding, pred_embedding)
# Return True if similarity is above threshold (0.7)
return similarity > 0.7
except Exception:
# Fallback to simple string comparison
truth_text = getattr(truth, 'grug_text', str(truth)).lower().strip()
pred_text = getattr(pred, 'grug_text', str(pred)).lower().strip()
return truth_text in pred_text or pred_text in truth_text
def ari_metric(truth: Any, pred: Any, trace: Any = None) -> bool:
"""Check if ARI is appropriate for Grug speak (simple language)"""
pred_text = getattr(pred, 'grug_text', str(pred))
pred_ari = automated_readability_index(pred_text)
# Grug speak should have low ARI (simple language)
# Return True if ARI is <= 7.01 (elementary school level)
return pred_ari <= 7.01
def overall_metric(provided_example: Any, predicted: Any, trace: Any = None, model: str = "llama3.2") -> bool:
"""Combined metric that checks both similarity and readability"""
similarity = similarity_metric(provided_example, predicted, trace, model)
ari = ari_metric(provided_example, predicted, trace)
# Both metrics must pass for overall success
return similarity and ari
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment