14 Commits

Author SHA1 Message Date
kennethreitz f4de0049f9 Update copyright format and bump version to 0.1.4 in documentation configuration 2024-10-30 18:33:36 -04:00
kennethreitz 524869668d Bump version to 0.1.4 and update CHANGELOG to introduce Session class for managing multiple conversations 2024-10-30 18:33:30 -04:00
kennethreitz a589850288 Clarify usage of Session class in README by specifying its role in setting default parameters for API calls 2024-10-30 18:29:42 -04:00
kennethreitz 4f38b44145 Simplify Session class usage example in README for clarity 2024-10-30 18:29:18 -04:00
kennethreitz 4babdcebd9 Add usage examples for Session class in README to demonstrate reduced repetition 2024-10-30 18:28:25 -04:00
kennethreitz 8474f101f2 Add Session class to manage API call configurations and enhance conversation creation 2024-10-30 18:24:45 -04:00
kennethreitz e9e47e27a1 Refactor BasePlugin class to remove ABC inheritance and abstract method decorators 2024-10-30 18:02:03 -04:00
kennethreitz 2309c30b8f Refactor find_provider function to remove Optional type and adjust return type to BaseProvider; update import statements for consistency 2024-10-30 18:00:26 -04:00
kennethreitz d972f1cd85 Refactor find_provider function to use Optional type for provider_name and specify return type as Type[BaseProvider] 2024-10-30 17:49:40 -04:00
kennethreitz e34f9b106c Enhance docstring for find_provider function to include parameters, return type, and exceptions 2024-10-30 17:48:06 -04:00
kennethreitz 1405c3bbb0 two llms talking 2024-10-30 10:21:02 -04:00
kennethreitz 624c132a59 Refactor README.md to update beta notice 2024-10-30 09:29:29 -04:00
kennethreitz 63a0fea60a Refactor README.md to update beta notice 2024-10-30 09:29:10 -04:00
kennethreitz 7b794930ac Refactor README.md to add beta notice 2024-10-30 09:28:55 -04:00
10 changed files with 176 additions and 28 deletions
+4
View File
@@ -1,6 +1,10 @@
Release History
===============
## 0.1.4 (2024-10-30)
- Introduce `Session` class to manage multiple conversations.
- General improvements.
## 0.1.3 (2024-10-30)
+29 -1
View File
@@ -2,12 +2,16 @@
[![Auto Wiki](https://img.shields.io/badge/Auto_Wiki-Mutable.ai-blue)](https://mutable.ai/kennethreitz/simplemind)
SimpleMind is an AI library designed to simplify your experience with AI APIs in Python. Inspired by a "for humans" philosophy, it abstracts away complexity, giving developers an intuitive and human-friendly way to interact with powerful AI capabilities. With SimpleMind, tapping into AI is as easy as a friendly conversation.
SimpleMind is an AI library designed to simplify your experience with AI APIs in Python. Inspired by a "for humans" philosophy, it abstracts away complexity, giving developers an intuitive and human-friendly way to interact with powerful AI capabilities.
With SimpleMind, tapping into AI is as easy as a friendly conversation.
```bash
$ pip install simplemind
```
**Note**: SimpleMind is currently in beta. We welcome feedback and contributions to help make it even better.
## Features
- **Easy-to-use AI tools**: SimpleMind provides simple interfaces to popular AI services.
- **Human-centered design**: The library prioritizes readability and usability—no need to be an expert to start experimenting.
@@ -105,6 +109,30 @@ To continue the conversation, you can call `conversation.send()` again, which re
<Message role=assistant text="The meaning of life is a profound philosophical question that has been explored by cultures, religions, and philosophers for centuries. Different people and belief systems offer varying interpretations:\n\n1. **Religious Perspectives:** Many religions propose that the meaning of life is to fulfill a divine purpose, serve God, or reach an afterlife. For example, Christianity often emphasizes love, faith, and service to God and others as central to lifes meaning.\n\n2. **Philosophical Views:** Philosophers offer diverse answers. Existentialists like Jean-Paul Sartre argue that life has no inherent meaning, and it is up to individuals to create their own purpose. Others, like Aristotle, suggest that achieving eudaimonia (flourishing or happiness) through virtuous living is the key to a meaningful life.\n\n3. **Scientific and Secular Approaches:** Some people find meaning through understanding the natural world, contributing to human knowledge, or through personal accomplishments and happiness. They may view lifes meaning as a product of connection, legacy, or the pursuit of knowledge and creativity.\n\n4. **Personal Perspective:** For many, the meaning of life is deeply personal, involving their relationships, passions, and goals. These individuals define lifes purpose through experiences, connections, and the impact they have on others and the world.\n\nUltimately, the meaning of life is a subjective question, with each person finding their own answers based on their beliefs, experiences, and reflections.">
```
### Stop Repeating Yourself
You can use the `Session` class to set default parameters for all calls:
```python
import simplemind as sm
# Create a session with defaults
gpt_4o_mini = sm.Session(llm_provider="openai", llm_model="gpt-4o-mini")
# Now all calls use these defaults
response = gpt_4o_mini.generate_text("Hello!")
conversation = gpt_4o_mini.create_conversation()
```
This maintains the simplicity of the original API while reducing repetition. The session object also supports overriding defaults on a per-call basis:
```python
response = gpt_4o_mini.generate_text(
"Complex task here",
llm_model="gpt-4"
)
```
### Basic Memory Plugin
Harnessing the power of Python, you can easily create your own plugins to add additional functionality to your conversations:
+2 -2
View File
@@ -14,9 +14,9 @@ sys.path.insert(0, os.path.abspath(".."))
import simplemind
project = "simplemind"
copyright = "2024, Kenneth Reitz"
copyright = "2024 Kenneth Reitz"
author = "Kenneth Reitz"
release = "v0.1.3"
release = "v0.1.4"
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+58
View File
@@ -0,0 +1,58 @@
import simplemind as sm
import time
class ConversationPlugin(sm.BasePlugin):
def post_send_hook(self, conversation, response):
# Print the LLM model and the response text.
print(f"{conversation.llm_model}:\n{response.text.strip()}\n\n------------\n")
def have_conversation(rounds=3):
# Create two conversations - one for each AI
with (
sm.create_conversation(
llm_model="claude-3-5-sonnet-20241022", llm_provider="anthropic"
) as claude_conv,
sm.create_conversation(
llm_model="llama3.2", llm_provider="ollama"
) as llama_conv,
):
# Add our plugin to both
plugin = ConversationPlugin()
claude_conv.add_plugin(plugin)
llama_conv.add_plugin(plugin)
# Start the conversation
prompt = "What do you think about the future of artificial intelligence? Please keep your response brief."
claude_conv.add_message("user", prompt, meta={})
claude_response = claude_conv.send()
# Have them discuss back and forth
for _ in range(rounds):
# Llama responds to Claude
llama_conv.add_message(
"user",
f"Respond to this statement from another AI: {claude_response.text}",
meta={},
)
llama_response = llama_conv.send()
time.sleep(1) # Add a small delay between responses
# Claude responds to Llama
claude_conv.add_message(
"user",
f"Respond to this statement from another AI: {llama_response.text}",
meta={},
)
claude_response = claude_conv.send()
time.sleep(1)
if __name__ == "__main__":
print("Starting AI conversation...\n")
have_conversation()
print("\nConversation ended.")
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "simplemind"
version = "0.1.3"
version = "0.1.4"
description = "An experimental client for AI providers that intends to replace LangChain and LangGraph for most common use cases."
readme = "README.md"
requires-python = ">=3.11"
+52 -2
View File
@@ -1,10 +1,59 @@
from typing import List, Optional
from typing import List, Optional, Type
from .models import Conversation, BasePlugin
from .models import Conversation, BasePlugin, BaseModel
from .utils import find_provider
from .settings import settings
class Session:
"""A session object that maintains configuration across multiple API calls.
Similar to `requests.Session`, this allows you to specify default settings
that will be used for all operations within the session.
"""
def __init__(
self,
*,
llm_provider: str = settings.DEFAULT_LLM_PROVIDER,
llm_model: str = settings.DEFAULT_LLM_MODEL,
**kwargs,
):
self.llm_provider = llm_provider
self.llm_model = llm_model
self.default_kwargs = kwargs
def generate_text(self, prompt: str, **kwargs) -> str:
"""Generate text using the session's default provider and model."""
merged_kwargs = {**self.default_kwargs, **kwargs}
return generate_text(
prompt=prompt,
llm_provider=self.llm_provider,
llm_model=self.llm_model,
**merged_kwargs,
)
def generate_data(
self, prompt: str, response_model: Type[BaseModel], **kwargs
) -> BaseModel:
"""Generate structured data using the session's default provider and model."""
merged_kwargs = {**self.default_kwargs, **kwargs}
return generate_data(
prompt=prompt,
response_model=response_model,
llm_provider=self.llm_provider,
llm_model=self.llm_model,
**merged_kwargs,
)
def create_conversation(self, **kwargs) -> "Conversation":
"""Create a conversation using the session's default provider and model."""
merged_kwargs = {**self.default_kwargs, **kwargs}
return create_conversation(
llm_provider=self.llm_provider, llm_model=self.llm_model, **merged_kwargs
)
def create_conversation(
llm_model=None, llm_provider=None, *, plugins: Optional[List[BasePlugin]] = None
):
@@ -53,4 +102,5 @@ __all__ = [
"generate_text",
"settings",
"BasePlugin",
"Session",
]
+1 -6
View File
@@ -22,33 +22,28 @@ class SMBaseModel(BaseModel):
return str(self)
class BasePlugin(ABC):
class BasePlugin:
"""The base conversation plugin class."""
# Plugin metadata.
meta: Dict[str, Any] = {}
# @abstractmethod
def initialize_hook(self, conversation: "Conversation"):
"""Initialize a hook for the plugin."""
raise NotImplementedError
# @abstractmethod
def cleanup_hook(self, conversation: "Conversation"):
"""Cleanup a hook for the plugin."""
raise NotImplementedError
# @abstractmethod
def add_message_hook(self, conversation: "Conversation", message: "Message"):
"""Add a message hook for the plugin."""
raise NotImplementedError
# @abstractmethod
def pre_send_hook(self, conversation: "Conversation"):
"""Pre-send hook for the plugin."""
raise NotImplementedError
# @abstractmethod
def post_send_hook(self, conversation: "Conversation", response: "Message"):
"""Post-send hook for the plugin."""
raise NotImplementedError
+6 -6
View File
@@ -1,10 +1,10 @@
from typing import List, Type
from simplemind.providers._base import BaseProvider
from simplemind.providers.anthropic import Anthropic
from simplemind.providers.groq import Groq
from simplemind.providers.openai import OpenAI
from simplemind.providers.ollama import Ollama
from simplemind.providers.xai import XAI
from ._base import BaseProvider
from .anthropic import Anthropic
from .groq import Groq
from .openai import OpenAI
from .ollama import Ollama
from .xai import XAI
providers: List[Type[BaseProvider]] = [Anthropic, Groq, OpenAI, Ollama, XAI]
+1
View File
@@ -17,6 +17,7 @@ class Settings(BaseSettings):
)
XAI_API_KEY: Optional[SecretStr] = Field(None, description="API key for xAI")
DEFAULT_LLM_PROVIDER: str = Field("openai", description="The default LLM provider")
DEFAULT_LLM_MODEL: str = Field("gpt-4o-mini", description="The default LLM model")
model_config = SettingsConfigDict(
env_file=".env", env_file_encoding="utf-8", case_sensitive=True, extra="ignore"
+22 -10
View File
@@ -1,22 +1,34 @@
import difflib
from typing import Union
from typing import Optional, Type
from .providers import providers
from .providers import providers, BaseProvider
_PROVIDER_NAMES = [provider.NAME.lower() for provider in providers]
def find_provider(provider_name: Union[str, None]):
"""Find a provider by name."""
if provider_name:
for provider_class in providers:
if provider_class.NAME.lower() == provider_name.lower():
# Instantiate the provider
return provider_class()
def find_provider(provider_name: str) -> BaseProvider:
"""
Find and instantiate a provider by name.
Parameters:
provider_name (Union[str, None]): The name of the provider to find.
Returns:
An instance of the provider class if found.
Raises:
ValueError: If the provider is not found, with a suggestion for the closest match.
"""
# Find the provider by name.
for provider_class in providers:
if provider_class.NAME.lower() == provider_name.lower():
# Instantiate the provider
return provider_class()
# Find the closest match
provider_found = difflib.get_close_matches(
provider_name.lower(), _PROVIDER_NAMES, n=1
) # Show only one suggestion
)
if provider_found:
raise ValueError(