21 Commits

Author SHA1 Message Date
kennethreitz 75a42044e5 Refactor CHANGELOG.md and pyproject.toml to update version to 0.2.0 and add Amazon Bedrock provider 2024-11-01 08:58:08 -04:00
kennethreitz cc66dbf8e5 Refactor pyproject.toml to add botocore and boto3 dependencies 2024-11-01 08:56:18 -04:00
kennethreitz a174e60a1e Refactor README.md to remove duplicate entry for Amazon Bedrock 2024-11-01 08:54:37 -04:00
kennethreitz b03695f626 Refactor pyproject.toml to update dependencies 2024-11-01 08:54:24 -04:00
kennethreitz 082bc24e91 Refactor pyproject.toml to update dependencies 2024-11-01 08:54:24 -04:00
kennethreitz aca1b87180 Merge pull request #25 from SZubarev/feature/amazon-bedrock
Added Amazon Bedrock provider
2024-11-01 08:53:46 -04:00
kennethreitz 1ff4c5660e Merge branch 'main' into feature/amazon-bedrock 2024-11-01 08:53:39 -04:00
kennethreitz 241a7ab402 Refactor pyproject.toml to add logfire as a dependency 2024-11-01 08:50:39 -04:00
kennethreitz 76fa7521eb Refactor quantity field in RecipeIngredient model to use float instead of string 2024-11-01 08:49:19 -04:00
kennethreitz cbec2c5f6d special thanks 2024-11-01 08:48:39 -04:00
kennethreitz 34f463839c logfire 2024-11-01 08:46:44 -04:00
kennethreitz c648a922b4 Bump version to v0.1.7 in conf.py and pyproject.toml 2024-11-01 08:44:37 -04:00
kennethreitz 873f5ba5f8 Refactor logging configuration to enable/disable logging 2024-11-01 08:44:18 -04:00
kennethreitz 28a7b2f140 Refactor logging configuration to enable/disable logging 2024-11-01 08:42:08 -04:00
kennethreitz 173162e798 Refactor LoggingConfig methods for enabling and disabling logging 2024-11-01 08:39:14 -04:00
Stan Zubarev 75c42278a2 add parameter to env template 2024-10-31 20:55:56 -04:00
Stan Zubarev c25f1e1058 rename parameter 2024-10-31 20:50:57 -04:00
Stan Zubarev 2a5966eb10 fix tests 2024-10-31 20:50:42 -04:00
Stan Zubarev f19263d309 update reaadme 2024-10-31 20:49:13 -04:00
Stan Zubarev 25b742db1f remove profile 2024-10-31 19:50:51 -04:00
Stan Zubarev 8d83050a64 add Amazon Bedrock provider 2024-10-31 19:34:50 -04:00
12 changed files with 187 additions and 17 deletions
+1
View File
@@ -4,3 +4,4 @@ export GROQ_API_KEY=""
export OLLAMA_HOST_URL=""
export OPENAI_API_KEY=""
export XAI_API_KEY=""
export AMAZON_PROFILE_NAME=""
+12
View File
@@ -1,6 +1,18 @@
Release History
===============
## 0.2.0 (2024-11-01)
- Add Amazon Bedrock provider.
- Make all provider optional dependencies. Use `$ pip install 'simplemind[full]'` to install all providers.
- General improvements.
## 0.1.7 (2024-11-01)
- Add `logger` decorator.
- Add `sm.enable_logfire()` function.
- General improvements.
## 0.1.6 (2024-10-31)
- Add `sm.Plugin` syntax sugar.
+41 -4
View File
@@ -19,6 +19,7 @@ With Simplemind, tapping into AI is as easy as a friendly conversation.
To specify a specific provider or model, you can use the `llm_provider` and `llm_model` parameters when calling: `generate_text`, `generate_data`, or `create_conversation`. The APIs remain identital between all supported providers/models.
- [**Anthropic's Claude**](https://www.anthropic.com/claude)
- [**Amazon Bedrock**](https://aws.amazon.com/bedrock/)
- [**Google's Gemini**](https://gemini.google/)
- [**Groq's Groq**](https://groq.com/)
- [**Ollama**](https://ollama.com)
@@ -28,6 +29,7 @@ To specify a specific provider or model, you can use the `llm_provider` and `llm
If you want to see Simplemind support, additional providers or models, please send a pull request!
## Why SimpleMind?
- **Intuitive**: Built with Pythonic simplicity and readability in mind.
- **For Humans**: Emphasizes a human-friendly interface, just like `requests` for HTTP.
- **Open Source**: Simplemind is open source, and contributions are always welcome!
@@ -39,7 +41,7 @@ Also, why not? :)
Simplemind takes care of the complex API calls so you can focus on what matters—building, experimenting, and creating.
```bash
$ pip install simplemind
$ pip install 'simplemind[full]'
```
First, authenticate your API keys by setting them in the environment variables:
@@ -56,7 +58,6 @@ Next, import Simplemind and start using it:
import simplemind as sm
```
## Examples
Here are some examples of how to use Simplemind:
@@ -90,6 +91,33 @@ class Poem(BaseModel):
title='Eternal Embrace' content='In the quiet hours of the night,\nWhen stars whisper secrets bright,\nTwo hearts beat in a gentle rhyme,\nDancing through the sands of time.\n\nWith every glance, a spark ignites,\nA flame that warms the coldest nights,\nIn laughter shared and whispers sweet,\nLove paints the world, a masterpiece.\n\nThrough stormy skies and sunlit days,\nIn myriad forms, it finds its ways,\nA tender touch, a knowing sigh,\nIn loves embrace, we learn to fly.\n\nAs seasons change and moments fade,\nIn the tapestry of dreams weve laid,\nLoves threads endure, forever bind,\nA timeless bond, two souls aligned.\n\nSo heres to love, both bright and true,\nA gift we give, anew, anew,\nIn every heartbeat, every prayer,\nA story written in the air.'
```
#### A more complex example
```python
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]
recipe = sm.generate_data(
"Write a recipe for chocolate chip cookies",
llm_model="gpt-4o-mini",
llm_provider="openai",
response_model=Recipe,
)
```
Special thanks to [@jxnl](https://github.com/jxnl) for building [Instructor](https://github.com/jxnl/instructor), which makes this possible!
### Conversational AI
SimpleMind also allows for easy conversational flows:
@@ -163,6 +191,7 @@ conversation.add_message(
text="Please write a poem about the moon",
)
```
```pycon
>>> conversation.send()
In the vast expanse where stars do play,
@@ -198,11 +227,18 @@ The universe is never done.
Simple, yet effective.
### Logging
Simplemind uses [logfire](https://logfire.ai) for logging. To enable logging, call `sm.enable_logfire()`.
### More Examples
Please see the [examples](examples) directory for executable examples.
-------------------
---
## Contributing
We welcome contributions of all kinds. Feel free to open issues for bug reports or feature requests, and submit pull requests to make SimpleMind even better.
To get started:
@@ -213,8 +249,9 @@ To get started:
4. Submit a pull request.
## License
Simplemind is licensed under the Apache 2.0 License.
## Acknowledgements
Simplemind is inspired by the philosophy of "code for humans" and aims to make working with AI models accessible to all. Special thanks to the open-source community for their contributions and inspiration.
Simplemind is inspired by the philosophy of "code for humans" and aims to make working with AI models accessible to all. Special thanks to the open-source community for their contributions and inspiration.
+1 -1
View File
@@ -16,7 +16,7 @@ import simplemind
project = "simplemind"
copyright = "2024 Kenneth Reitz"
author = "Kenneth Reitz"
release = "v0.1.6"
release = "v0.2.0"
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+13 -2
View File
@@ -1,10 +1,21 @@
[project]
name = "simplemind"
version = "0.1.6"
version = "0.2.0"
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.10"
dependencies = ["pydantic", "pydantic-settings", "instructor", "openai", "anthropic", "ollama", "groq", "google-generativeai"]
dependencies = ["pydantic", "pydantic-settings", "instructor", "logfire"]
[project.optional-dependencies]
full = [
"openai",
"anthropic",
"ollama",
"groq",
"google-generativeai",
"botocore",
"boto3"
]
[build-system]
requires = ["hatchling"]
+6
View File
@@ -113,6 +113,11 @@ def generate_text(
return provider.generate_text(prompt=prompt, llm_model=llm_model, **kwargs)
def enable_logfire() -> None:
"""Enable logfire logging."""
settings.logging.enable_logfire()
# Syntax sugar.
Plugin = BasePlugin
@@ -125,4 +130,5 @@ __all__ = [
"BasePlugin",
"Session",
"Plugin",
"enable_logfire",
]
+8 -2
View File
@@ -7,18 +7,24 @@ from .settings import settings
def logger(func: Callable[..., Any]) -> Callable[..., Any]:
"""A @logger decorator that logs the function parameters, function returns, and exceptions raised if logging is enabled."""
"""A decorator that logs the function parameters, function returns,
and exceptions raised if logging is enabled, using logfire.
"""
def wrapper(*args, **kwargs) -> Any:
if not settings.logging.enabled:
if not settings.logging.is_enabled:
return func(*args, **kwargs)
logfire.info(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
t1 = time.perf_counter()
try:
result = func(*args, **kwargs)
t2 = time.perf_counter()
logfire.info(f"{func.__name__} returned: {result} in {t2-t1} seconds")
return result
except Exception as e:
t2 = time.perf_counter()
logfire.error(f"Error in {func.__name__}: {e} in {t2-t1} seconds")
+2 -1
View File
@@ -7,5 +7,6 @@ from .groq import Groq
from .ollama import Ollama
from .openai import OpenAI
from .xai import XAI
from .amazon import Amazon
providers: List[Type[BaseProvider]] = [Anthropic, Gemini, Groq, OpenAI, Ollama, XAI]
providers: List[Type[BaseProvider]] = [Anthropic, Gemini, Groq, OpenAI, Ollama, XAI, Amazon]
+90
View File
@@ -0,0 +1,90 @@
from typing import Type, TypeVar
import instructor
import anthropic
from pydantic import BaseModel
from ._base import BaseProvider
from ..settings import settings
T = TypeVar("T", bound=BaseModel)
PROVIDER_NAME = "amazon"
DEFAULT_MODEL = "anthropic.claude-3-sonnet-20240229-v1:0"
DEFAULT_MAX_TOKENS = 5_000
class Amazon(BaseProvider):
NAME = PROVIDER_NAME
DEFAULT_MODEL = DEFAULT_MODEL
def __init__(self, profile_name: str | None = None):
self.profile_name = profile_name or settings.AMAZON_PROFILE_NAME
@property
def client(self):
"""The AnthropicBedrock client."""
if not self.profile_name:
raise ValueError("Profile name is not provided")
return anthropic.AnthropicBedrock(aws_profile=self.profile_name)
@property
def structured_client(self):
"""A client patched with Instructor."""
return instructor.from_anthropic(self.client)
def send_conversation(self, conversation: "Conversation", **kwargs):
"""Send a conversation to the OpenAI API."""
from ..models import Message
messages = [
{"role": msg.role, "content": msg.text} for msg in conversation.messages
]
response = self.client.chat.completions.create(
model=conversation.llm_model or DEFAULT_MODEL, messages=messages, **kwargs
)
# Get the response content from the OpenAI response
assistant_message = response.choices[0].message
# Create and return a properly formatted Message instance
return Message(
role="assistant",
text=assistant_message.content or "",
raw=response,
llm_model=conversation.llm_model or DEFAULT_MODEL,
llm_provider=PROVIDER_NAME,
)
def structured_response(
self, prompt, response_model: Type[T], *, llm_model: str | None = None, **kwargs
) -> T:
# Ensure messages are provided in kwargs
messages = [
{"role": "user", "content": prompt},
]
response = self.structured_client.chat.completions.create(
messages=messages,
model=llm_model or self.DEFAULT_MODEL,
response_model=response_model,
max_tokens=DEFAULT_MAX_TOKENS,
**kwargs,
)
return response
def generate_text(self, prompt, *, llm_model, **kwargs):
messages = [
{"role": "user", "content": prompt},
]
response = self.client.messages.create(
model=llm_model or self.DEFAULT_MODEL,
messages=messages,
max_tokens=DEFAULT_MAX_TOKENS,
**kwargs,
)
return response.content[0].text
+5 -4
View File
@@ -7,7 +7,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
class LoggingConfig(BaseSettings):
"""The class that holds all the logging settings for the application."""
enabled: bool = Field(False, description="Enable logging")
is_enabled: bool = Field(False, description="Enable logging")
model_config = SettingsConfigDict(extra="forbid")
@@ -22,7 +22,7 @@ class LoggingConfig(BaseSettings):
"To enable logging, please install logfire: `pip install logfire`"
) from e
self.enabled = True
self.is_enabled = True
logfire.configure(**kwargs)
basicConfig(handlers=[logfire.LogfireLoggingHandler()])
@@ -30,17 +30,18 @@ class LoggingConfig(BaseSettings):
logfire.configure(**kwargs)
basicConfig(handlers=[logfire.LogfireLoggingHandler()])
except Exception as e:
self.enabled = False # Reset flag on failure
self.is_enabled = False # Reset flag on failure
raise RuntimeError("Failed to configure logging") from e
def disable_logfire(self) -> None:
"""Disable logging for the application."""
self.enabled = False
self.is_enabled = False
class Settings(BaseSettings):
"""The class that holds all the API keys for the application."""
AMAZON_PROFILE_NAME: Optional[str] = Field("default", description="AWS Named Profile")
ANTHROPIC_API_KEY: Optional[SecretStr] = Field(
None, description="API key for Anthropic"
)
+5 -2
View File
@@ -1,7 +1,9 @@
from pydantic import BaseModel
import pytest
from simplemind.providers import Anthropic, Gemini, Groq, Ollama, OpenAI
from simplemind.providers import Anthropic, Gemini, OpenAI, Groq, Ollama, Amazon
from pydantic import BaseModel
class ResponseModel(BaseModel):
@@ -16,6 +18,7 @@ class ResponseModel(BaseModel):
OpenAI,
Groq,
Ollama,
Amazon
],
)
def test_generate_data(provider_cls):
+3 -1
View File
@@ -1,5 +1,6 @@
import pytest
from simplemind.providers import Anthropic, Gemini, Groq, Ollama, OpenAI
from simplemind.providers import Anthropic, Gemini, OpenAI, Groq, Ollama, Amazon
@pytest.mark.parametrize(
@@ -10,6 +11,7 @@ from simplemind.providers import Anthropic, Gemini, Groq, Ollama, OpenAI
OpenAI,
Groq,
Ollama,
Amazon,
],
)
def test_generate_text(provider_cls):