diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d83c0b..e7484b4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,11 @@
Release History
===============
+## 0.2.3 (2024-11-03)
+
+- Update default model for Amazon provider.
+- Improved logging to handle streaming functions.
+
## 0.2.2 (2024-11-02)
- Add streaming support (set `stream=True` to `generate_text`).
diff --git a/README.md b/README.md
index e60c460..acac77d 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ The APIs remain identical between all supported providers / models:
| Amazon's Bedrock |
"amazon" |
- "anthropic.claude-3-sonnet-20240229-v1:0" |
+ "anthropic.claude-3-5-sonnet-20241022-v2:0" |
| Google's Gemini |
@@ -93,7 +93,7 @@ import simplemind as sm
## Examples
-Here are some examples of how to use Simplemind.
+Here are some examples of how to use Simplemind.
**Please note**: Most of the calls seen here optionally accept `llm_provider` and `llm_model` parameters, which you provide as strings.
@@ -188,7 +188,7 @@ response = gpt_4o_mini.generate_text("Hello!")
conversation = gpt_4o_mini.create_conversation()
```
-This maintains the simplicity of the original API while reducing repetition.
+This maintains the simplicity of the original API while reducing repetition.
The session object also supports overriding defaults on a per-call basis:
diff --git a/examples/cooking_recipe_example.py b/examples/cooking_recipe_example.py
index 1d8990c..8abc295 100644
--- a/examples/cooking_recipe_example.py
+++ b/examples/cooking_recipe_example.py
@@ -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.
-
diff --git a/examples/discussion.py b/examples/discussion.py
index 3c78a6b..d63353d 100644
--- a/examples/discussion.py
+++ b/examples/discussion.py
@@ -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)
diff --git a/examples/web_bible_explorer.py b/examples/web_bible_explorer.py
new file mode 100644
index 0000000..4012078
--- /dev/null
+++ b/examples/web_bible_explorer.py
@@ -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
diff --git a/simplemind/providers/amazon.py b/simplemind/providers/amazon.py
index 87d0b3c..e286723 100644
--- a/simplemind/providers/amazon.py
+++ b/simplemind/providers/amazon.py
@@ -10,7 +10,7 @@ from ..settings import settings
T = TypeVar("T", bound=BaseModel)
PROVIDER_NAME = "amazon"
-DEFAULT_MODEL = "anthropic.claude-3-sonnet-20240229-v1:0"
+DEFAULT_MODEL = "anthropic.claude-3-5-sonnet-20241022-v2:0"
DEFAULT_MAX_TOKENS = 5_000