mirror of
https://github.com/kennethreitz/simplemind.git
synced 2026-06-05 14:50:16 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f4de0049f9 | |||
| 524869668d | |||
| a589850288 | |||
| 4f38b44145 | |||
| 4babdcebd9 | |||
| 8474f101f2 | |||
| e9e47e27a1 | |||
| 2309c30b8f | |||
| d972f1cd85 | |||
| e34f9b106c | |||
| 1405c3bbb0 | |||
| 624c132a59 | |||
| 63a0fea60a | |||
| 7b794930ac |
@@ -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)
|
||||
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
|
||||
[](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 life’s 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 life’s 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 life’s 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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user