mirror of
https://github.com/kennethreitz/simplemind.git
synced 2026-06-05 06:46:18 +00:00
Merge branch 'kennethreitz:main' into main
This commit is contained in:
@@ -1,34 +1,68 @@
|
||||
from pydantic import BaseModel
|
||||
import simplemind as sm
|
||||
from _context import simplemind as sm
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.text import Text
|
||||
|
||||
|
||||
class InstructionStep(BaseModel):
|
||||
step_number: int
|
||||
instruction: str
|
||||
|
||||
|
||||
class RecipeIngredient(BaseModel):
|
||||
name: str
|
||||
quantity: float
|
||||
unit: str
|
||||
|
||||
|
||||
class Recipe(BaseModel):
|
||||
name: str
|
||||
ingredients: list[RecipeIngredient]
|
||||
instructions: list[InstructionStep]
|
||||
|
||||
|
||||
def __str__(self) -> str:
|
||||
output = f"\n=== {self.name.upper()} ===\n\n"
|
||||
|
||||
output += "INGREDIENTS:\n"
|
||||
console = Console(record=True, file=None)
|
||||
|
||||
# Create formatted title with more emphasis
|
||||
title = Text("✨ " + self.name.upper() + " ✨", style="bold blue")
|
||||
|
||||
# Format ingredients with better structure
|
||||
ingredients_text = Text("\n📝 INGREDIENTS:\n", style="bold green")
|
||||
for ing in self.ingredients:
|
||||
output += f"• {ing.quantity} {ing.unit} {ing.name}\n"
|
||||
|
||||
output += "\nINSTRUCTIONS:\n"
|
||||
# Format numbers to avoid floating decimals when whole numbers
|
||||
quantity = int(ing.quantity) if ing.quantity.is_integer() else ing.quantity
|
||||
ingredients_text.append(f" • {quantity} {ing.unit} ", style="bright_white")
|
||||
ingredients_text.append(f"{ing.name}\n", style="italic bright_white")
|
||||
|
||||
# Format instructions with better spacing and styling
|
||||
instructions_text = Text("\n👩🍳 INSTRUCTIONS:\n", style="bold yellow")
|
||||
for step in self.instructions:
|
||||
output += f"{step.step_number}. {step.instruction}\n"
|
||||
|
||||
return output
|
||||
|
||||
instructions_text.append(
|
||||
f"\n {step.step_number}. ", style="bold bright_white"
|
||||
)
|
||||
instructions_text.append(f"{step.instruction}", style="bright_white")
|
||||
|
||||
# Combine all text
|
||||
full_text = Text.assemble(
|
||||
ingredients_text, instructions_text, "\n"
|
||||
) # Added extra newline
|
||||
|
||||
# Create panel with enhanced styling
|
||||
panel = Panel(
|
||||
full_text,
|
||||
title=title,
|
||||
border_style="blue",
|
||||
padding=(1, 2), # Add padding (vertical, horizontal)
|
||||
expand=False, # Don't expand to full terminal width
|
||||
title_align="center",
|
||||
)
|
||||
|
||||
# Render the panel to string without printing
|
||||
with console.capture() as capture:
|
||||
console.print(panel)
|
||||
return capture.get()
|
||||
|
||||
|
||||
recipe = sm.generate_data(
|
||||
"Write a recipe for chocolate chip cookies",
|
||||
@@ -63,4 +97,3 @@ print(recipe)
|
||||
# 7. Drop by rounded tablespoon onto ungreased cookie sheets.
|
||||
# 8. Bake for 9 to 11 minutes, or until edges are golden.
|
||||
# 9. Let cool on the cookie sheet for a few minutes before transferring to wire racks to cool completely.
|
||||
|
||||
|
||||
+30
-31
@@ -11,14 +11,6 @@ class MultiAIConversation:
|
||||
"""Orchestrates conversations between multiple AI models."""
|
||||
|
||||
MODEL_SESSIONS = {
|
||||
"Llama3.2": sm.Session(
|
||||
llm_provider="ollama",
|
||||
llm_model="llama3.2",
|
||||
),
|
||||
"Claude-3.5-Sonnet": sm.Session(
|
||||
llm_provider="anthropic",
|
||||
llm_model="claude-3-5-sonnet-20241022",
|
||||
),
|
||||
"GPT-4o": sm.Session(
|
||||
llm_provider="openai",
|
||||
llm_model="gpt-4o",
|
||||
@@ -27,6 +19,10 @@ class MultiAIConversation:
|
||||
llm_provider="xai",
|
||||
llm_model="grok-beta",
|
||||
),
|
||||
"Claude-3.5-Sonnet": sm.Session(
|
||||
llm_provider="anthropic",
|
||||
llm_model="claude-3-5-sonnet-20241022",
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self, topic: str, turns_per_model: int = 1, max_rounds: int = 5):
|
||||
@@ -36,13 +32,14 @@ class MultiAIConversation:
|
||||
self.max_rounds = max_rounds
|
||||
self.conversation_history: List[Tuple[str, str]] = []
|
||||
self.console = Console()
|
||||
self.user_name = "Kenneth Reitz"
|
||||
|
||||
def _format_system_prompt(self, ai_name: str) -> str:
|
||||
"""Creates a system prompt for each AI model."""
|
||||
return f"""You are {ai_name}. You are participating in a thoughtful discussion with other AI models about {self.topic}.
|
||||
|
||||
Rules:
|
||||
1. Be concise but insightful (keep responses under 100 words)
|
||||
1. Be concise but insightful (keep responses under 140 words)
|
||||
2. Build upon previous points made in the conversation
|
||||
3. Ask questions to deepen the discussion when appropriate
|
||||
4. Stay on topic while maintaining your unique perspective
|
||||
@@ -72,32 +69,31 @@ Current discussion topic: {self.topic}"""
|
||||
# Store in history
|
||||
self.conversation_history.append((ai_name, response))
|
||||
|
||||
def _get_user_input(self) -> str:
|
||||
"""Gets input from the user for the discussion."""
|
||||
self.console.print("\n[bold green]Your turn! Share your thoughts:[/bold green]")
|
||||
user_response = input("> ")
|
||||
self._print_response(self.user_name, user_response)
|
||||
return user_response
|
||||
|
||||
def run_conversation(self):
|
||||
"""Runs the multi-AI conversation."""
|
||||
|
||||
# Initialize the conversation
|
||||
initial_prompt = (
|
||||
f"Let's have a thoughtful discussion about {self.topic}. "
|
||||
"Please share your initial thoughts in 2-3 sentences."
|
||||
# Get initial thoughts from the human
|
||||
self.console.print(
|
||||
f"\n[bold green]Start the discussion about {self.topic}:[/bold green]"
|
||||
)
|
||||
self._get_user_input()
|
||||
|
||||
for round_num in range(self.max_rounds):
|
||||
self.console.print(f"\n[bold green]Round {round_num + 1}[/bold green]")
|
||||
|
||||
# Let all AI models respond
|
||||
for model_name, session in self.MODEL_SESSIONS.items():
|
||||
for turn in range(self.turns_per_model):
|
||||
conversation = self._create_conversation(session, model_name)
|
||||
|
||||
# Add the prompt
|
||||
prompt = (
|
||||
initial_prompt
|
||||
if round_num == 0 and turn == 0
|
||||
else (
|
||||
f"Continue the discussion about {self.topic}, "
|
||||
"responding to the previous points made."
|
||||
)
|
||||
)
|
||||
|
||||
# Add the prompt (simplified since human always starts)
|
||||
prompt = f"Continue the discussion about {self.topic}, responding to the previous points made."
|
||||
conversation.add_message(role="user", text=prompt)
|
||||
|
||||
# Get and print response
|
||||
@@ -107,17 +103,24 @@ Current discussion topic: {self.topic}"""
|
||||
# Small delay to prevent rate limiting
|
||||
time.sleep(1)
|
||||
|
||||
# Then get user input at the end of the round
|
||||
self._get_user_input()
|
||||
|
||||
# Optional: Add a separator between rounds
|
||||
self.console.print("\n" + "-" * 50)
|
||||
|
||||
|
||||
def have_ai_discussion(topic: str, turns_per_model: int = 1, max_rounds: int = 3):
|
||||
def have_ai_discussion(turns_per_model: int = 1, max_rounds: int = 3):
|
||||
"""Convenience function to start an AI discussion."""
|
||||
# Get topic from user
|
||||
print("\nWhat topic would you like to discuss?")
|
||||
topic = input("> ")
|
||||
|
||||
debate = MultiAIConversation(
|
||||
topic=topic, turns_per_model=turns_per_model, max_rounds=max_rounds
|
||||
)
|
||||
|
||||
print(f"\nStarting AI discussion on: {topic}")
|
||||
print(f"\nStarting AI discussion about: {topic}")
|
||||
print("=" * 50)
|
||||
|
||||
debate.run_conversation()
|
||||
@@ -125,8 +128,4 @@ def have_ai_discussion(topic: str, turns_per_model: int = 1, max_rounds: int = 3
|
||||
|
||||
# Example usage
|
||||
if __name__ == "__main__":
|
||||
# Example topics
|
||||
topic = "The future of human-AI collaboration in creative fields",
|
||||
|
||||
# Run a discussion on the first topic
|
||||
have_ai_discussion(topic=topic, turns_per_model=1, max_rounds=3)
|
||||
have_ai_discussion(turns_per_model=1, max_rounds=5)
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
from fastapi import FastAPI, Request, HTTPException
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from pydantic import BaseModel
|
||||
from typing import List
|
||||
import simplemind as sm
|
||||
|
||||
app = FastAPI()
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
||||
class CrossReference(BaseModel):
|
||||
"""Model for cross references."""
|
||||
|
||||
verse_reference: str
|
||||
explanation: str
|
||||
relevance: str
|
||||
|
||||
|
||||
class BibleVerseAnalysis(BaseModel):
|
||||
"""Model for a Bible verse and its analysis."""
|
||||
|
||||
book: str
|
||||
chapter: int
|
||||
verse: int
|
||||
text: str
|
||||
historical_context: str
|
||||
theological_significance: str
|
||||
practical_application: str
|
||||
cross_references: List[CrossReference]
|
||||
|
||||
|
||||
# Bible data constants
|
||||
BIBLE_BOOKS = [
|
||||
# Old Testament
|
||||
"Genesis",
|
||||
"Exodus",
|
||||
"Leviticus",
|
||||
"Numbers",
|
||||
"Deuteronomy",
|
||||
"Joshua",
|
||||
"Judges",
|
||||
"Ruth",
|
||||
"1 Samuel",
|
||||
"2 Samuel",
|
||||
"1 Kings",
|
||||
"2 Kings",
|
||||
"1 Chronicles",
|
||||
"2 Chronicles",
|
||||
"Ezra",
|
||||
"Nehemiah",
|
||||
"Esther",
|
||||
"Job",
|
||||
"Psalms",
|
||||
"Proverbs",
|
||||
"Ecclesiastes",
|
||||
"Song of Solomon",
|
||||
"Isaiah",
|
||||
"Jeremiah",
|
||||
"Lamentations",
|
||||
"Ezekiel",
|
||||
"Daniel",
|
||||
"Hosea",
|
||||
"Joel",
|
||||
"Amos",
|
||||
"Obadiah",
|
||||
"Jonah",
|
||||
"Micah",
|
||||
"Nahum",
|
||||
"Habakkuk",
|
||||
"Zephaniah",
|
||||
"Haggai",
|
||||
"Zechariah",
|
||||
"Malachi",
|
||||
# New Testament
|
||||
"Matthew",
|
||||
"Mark",
|
||||
"Luke",
|
||||
"John",
|
||||
"Acts",
|
||||
"Romans",
|
||||
"1 Corinthians",
|
||||
"2 Corinthians",
|
||||
"Galatians",
|
||||
"Ephesians",
|
||||
"Philippians",
|
||||
"Colossians",
|
||||
"1 Thessalonians",
|
||||
"2 Thessalonians",
|
||||
"1 Timothy",
|
||||
"2 Timothy",
|
||||
"Titus",
|
||||
"Philemon",
|
||||
"Hebrews",
|
||||
"James",
|
||||
"1 Peter",
|
||||
"2 Peter",
|
||||
"1 John",
|
||||
"2 John",
|
||||
"3 John",
|
||||
"Jude",
|
||||
"Revelation",
|
||||
]
|
||||
|
||||
BIBLE_BOOK_CHAPTERS = {
|
||||
# Old Testament
|
||||
"Genesis": 50,
|
||||
"Exodus": 40,
|
||||
"Leviticus": 27,
|
||||
"Numbers": 36,
|
||||
"Deuteronomy": 34,
|
||||
"Joshua": 24,
|
||||
"Judges": 21,
|
||||
"Ruth": 4,
|
||||
"1 Samuel": 31,
|
||||
"2 Samuel": 24,
|
||||
"1 Kings": 22,
|
||||
"2 Kings": 25,
|
||||
"1 Chronicles": 29,
|
||||
"2 Chronicles": 36,
|
||||
"Ezra": 10,
|
||||
"Nehemiah": 13,
|
||||
"Esther": 10,
|
||||
"Job": 42,
|
||||
"Psalms": 150,
|
||||
"Proverbs": 31,
|
||||
"Ecclesiastes": 12,
|
||||
"Song of Solomon": 8,
|
||||
"Isaiah": 66,
|
||||
"Jeremiah": 52,
|
||||
"Lamentations": 5,
|
||||
"Ezekiel": 48,
|
||||
"Daniel": 12,
|
||||
"Hosea": 14,
|
||||
"Joel": 3,
|
||||
"Amos": 9,
|
||||
"Obadiah": 1,
|
||||
"Jonah": 4,
|
||||
"Micah": 7,
|
||||
"Nahum": 3,
|
||||
"Habakkuk": 3,
|
||||
"Zephaniah": 3,
|
||||
"Haggai": 2,
|
||||
"Zechariah": 14,
|
||||
"Malachi": 4,
|
||||
# New Testament
|
||||
"Matthew": 28,
|
||||
"Mark": 16,
|
||||
"Luke": 24,
|
||||
"John": 21,
|
||||
"Acts": 28,
|
||||
"Romans": 16,
|
||||
"1 Corinthians": 16,
|
||||
"2 Corinthians": 13,
|
||||
"Galatians": 6,
|
||||
"Ephesians": 6,
|
||||
"Philippians": 4,
|
||||
"Colossians": 4,
|
||||
"1 Thessalonians": 5,
|
||||
"2 Thessalonians": 3,
|
||||
"1 Timothy": 6,
|
||||
"2 Timothy": 4,
|
||||
"Titus": 3,
|
||||
"Philemon": 1,
|
||||
"Hebrews": 13,
|
||||
"James": 5,
|
||||
"1 Peter": 5,
|
||||
"2 Peter": 3,
|
||||
"1 John": 5,
|
||||
"2 John": 1,
|
||||
"3 John": 1,
|
||||
"Jude": 1,
|
||||
"Revelation": 22,
|
||||
}
|
||||
|
||||
|
||||
# Add a new endpoint to get chapter count
|
||||
@app.get("/chapters/{book}")
|
||||
async def get_chapter_count(book: str):
|
||||
if book in BIBLE_BOOK_CHAPTERS:
|
||||
return {"chapters": BIBLE_BOOK_CHAPTERS[book]}
|
||||
return {"chapters": 0}
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def home(request: Request):
|
||||
return templates.TemplateResponse(
|
||||
"index.html",
|
||||
{
|
||||
"request": request,
|
||||
"bible_books": BIBLE_BOOKS,
|
||||
"current_book": "Genesis",
|
||||
"current_chapter": 1,
|
||||
"current_verse": 1,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@app.get("/verse/{book}/{chapter}/{verse}")
|
||||
async def get_verse(book: str, chapter: int, verse: int):
|
||||
# Validate book and chapter
|
||||
if book not in BIBLE_BOOK_CHAPTERS:
|
||||
raise HTTPException(status_code=400, detail="Invalid book name")
|
||||
|
||||
if chapter < 1 or chapter > BIBLE_BOOK_CHAPTERS[book]:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Invalid chapter. {book} has {BIBLE_BOOK_CHAPTERS[book]} chapters",
|
||||
)
|
||||
|
||||
prompt = f"""
|
||||
For {book} {chapter}:{verse}, provide:
|
||||
1. The ESV Bible text
|
||||
2. Analysis of the verse
|
||||
|
||||
Return in this exact format:
|
||||
{{
|
||||
"book": "{book}",
|
||||
"chapter": {chapter},
|
||||
"verse": {verse},
|
||||
"text": "The ESV Bible text",
|
||||
"historical_context": "brief historical background",
|
||||
"theological_significance": "main theological points",
|
||||
"practical_application": "how to apply this verse today",
|
||||
"cross_references": [
|
||||
{{
|
||||
"verse_reference": "Book Chapter:Verse",
|
||||
"explanation": "why this verse is related",
|
||||
"relevance": "how it connects to the main verse"
|
||||
}}
|
||||
]
|
||||
}}
|
||||
"""
|
||||
|
||||
data = sm.generate_data(prompt, response_model=BibleVerseAnalysis)
|
||||
|
||||
return data
|
||||
Reference in New Issue
Block a user