Skip to main content

A simple and elegant Python Mini SDK for Google Gemini AI.

Project description

PyPI Downloads

Dracula 🧛

A simple, elegant Python library and Mini SDK for Google Gemini with powerful features. Built for developers who want to integrate AI into their projects without dealing with complex API setup.

Installation

pip install dracula-ai

Quick Start

from dracula import Dracula, GeminiModel
from dotenv import load_dotenv
import os

load_dotenv()

ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"))
response = ai.chat("Hello, who are you?")
print(response)

Parameters

Parameter Type Default Description
api_key str required Your Google Gemini API key
model GeminiModel or str GeminiModel.FLASH Gemini model to use (default: gemini-2.5-flash)
max_messages int 10 Maximum number of messages to remember
prompt str "You are a helpful assistant." System prompt
temperature float 1.0 Response creativity (0.0 - 2.0)
max_output_tokens int 8192 Maximum response length
db_path str "~/.dracula/" Path to the SQLite memory and stats databases
language str "English" Language for responses
logging bool False Enable or disable logging
log_level str "DEBUG" Logging level
log_file str None Path to log file
log_max_bytes int 5MB Maximum log file size
log_backup_count int 5 Number of backup log files
tools list None List of tools to register
max_retries int 3 Maximum number of retry attempts for failed requests
retry_delay float 1.0 Base delay in seconds for exponential backoff

Features

Note: All examples below assume you have loaded your environment variables using load_dotenv() and os.

💬 Text Chat

The most basic feature of Dracula. Send a message to Gemini and get a response back. Every message you send and every response you receive is automatically stored in memory, so Gemini always knows the context of your conversation.

ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"))
response = ai.chat("What is Python?")
print(response)

🌊 Streaming

Normally, Dracula waits for Gemini to finish generating the full response before returning it. Streaming changes this behavior — instead of waiting, you receive the response word by word as it is being generated, just like ChatGPT does. This is especially useful for long responses or when you want a more interactive feel in your app.

for chunk in ai.stream("Tell me a long story."):
    print(chunk, end="", flush=True)

🧠 SQLite Conversation Memory

Dracula automatically remembers the conversation history so Gemini can refer back to previous messages. Since v0.8.0, all history is persisted in a high-performance SQLite database, preventing memory bloat and ensuring your AI never loses context, even if your program restarts.

ai.chat("My name is Ahmet.")
response = ai.chat("What is my name?")
print(response)  # It remembers! ✅

ai.clear_memory()  # Wipe memory

💾 Save & Load History

By default, conversation history only exists while your program is running. Once you stop the program, the history is lost. Save & Load History solves this by letting you save the conversation to a JSON file and reload it later, so your AI can continue right where it left off — even in a completely new run of your program.

ai.save_history("conversation.json")

# Later, in a new run of your program:
ai.load_history("conversation.json")

📜 Pretty Print History

get_history() returns the raw conversation history as a list of dictionaries, which can be hard to read. print_history() formats the same data into a clean, human-readable layout with clear labels for each message, making it much easier to follow the conversation at a glance.

ai.print_history()

🎭 System Prompt

The system prompt is a set of instructions you give to Gemini before the conversation starts. It defines the AI's personality, role, and behavior for the entire conversation. For example, you can tell it to act as a pirate, a chef, a formal assistant, or anything else you can imagine. The user will never see this prompt — it works silently in the background.

ai = Dracula(
    api_key=os.getenv("GEMINI_API_KEY"),
    prompt="You are a pirate who answers everything dramatically."
)

# You can also change it anytime during the conversation:
ai.set_prompt("You are now a formal assistant.")

🌡️ Temperature Control

Temperature controls how creative and random Gemini's responses are. A low temperature (close to 0.0) makes responses more focused, predictable, and factual — great for technical questions. A high temperature (close to 2.0) makes responses more creative, surprising, and varied — great for storytelling or brainstorming. The default value of 1.0 is a balanced middle ground.

ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"), temperature=0.2)  # Focused
ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"), temperature=1.8)  # Creative

# You can also change it anytime:
ai.set_temperature(0.5)

📏 Max Output Tokens

Tokens are small chunks of text — roughly one token per word. max_output_tokens controls the maximum length of Gemini's responses. If you want short, concise answers set it low. If you want long, detailed responses set it high. The default is 8192 which is large enough for most use cases.

ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"), max_output_tokens=256)  # Short
ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"), max_output_tokens=8192) # Long

# You can also change it anytime:
ai.set_max_output_tokens(512)

🌍 Response Language

By default Gemini responds in whatever language the user writes in. The language feature overrides this behavior and forces Gemini to always respond in a specific language, regardless of what language the user writes in. Use "Auto" to let Gemini detect the language automatically.

ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"), language="Turkish")
response = ai.chat("Hello!")
print(response)  # Merhaba! ✅

# Auto detect language
ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"), language="Auto")

# Change it anytime:
ai.set_language("Spanish")

🤖 GeminiModel Enum

Instead of typing model names as strings which can cause typos and break when Google updates model names, Dracula provides a GeminiModel enum with all available models. You can still use strings for advanced use cases, but the enum is the recommended approach.

from dracula import Dracula, GeminiModel

ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"), model=GeminiModel.FLASH)
ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"), model=GeminiModel.PRO)
ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"), model=GeminiModel.FLASH_LITE)

# Change model anytime:
ai.set_model(GeminiModel.PRO)

# Discover all available models in real time:
print(ai.list_available_models())

🛠️ Function Calling / Tools

Function calling is one of the most powerful features of Dracula. It lets you give Gemini access to your own Python functions — like checking the weather, searching the web, querying a database, or anything else you can write in Python. Gemini will automatically decide when and how to call your functions based on the user's message. Use auto_call=True to let Dracula handle everything automatically, or auto_call=False to handle tool calls yourself.

from dracula import Dracula, tool, ToolResult, GeminiModel

@tool(description="Get the current weather for a city")
def get_weather(city: str) -> str:
    return f"It's 25°C and sunny in {city}"

@tool(description="Search the web for information")
def search_web(query: str) -> str:
    return f"Search results for: {query}"

ai = Dracula(
    api_key=os.getenv("GEMINI_API_KEY"),
    tools=[get_weather, search_web]
)

# Auto call — Dracula handles everything automatically
response = ai.chat("What's the weather in Istanbul?")
print(response)
# "The weather in Istanbul is currently 25°C and sunny!"

# Manual call — you handle the tool call yourself
result = ai.chat("What's the weather in Ankara?", auto_call=False)
if result.requires_tool_call:
    print(f"Tool: {result.tool_name}")   # get_weather
    print(f"Args: {result.tool_args}")   # {"city": "Ankara"}

# Add tools after initialization
ai.add_tool(search_web)

# List registered tools
print(ai.list_tools())  # ["get_weather", "search_web"]

🛡️ Auto-Retry & Resilience

Network instability, rate limits (429), and temporary server errors (500, 503) are common when working with cloud AI models. Dracula includes a built-in, smart retry mechanism using exponential backoff. If a request fails due to a temporary issue, Dracula will automatically pause and try again, increasing the wait time between each attempt. This works seamlessly under the hood for both synchronous and asynchronous operations.

# Retries are handled automatically out of the box
ai = Dracula(
    api_key=os.getenv("GEMINI_API_KEY"),
    max_retries=3,      # Maximum number of retry attempts (default: 3)
    retry_delay=1.0     # Base delay in seconds, doubles each attempt (default: 1.0)
)

# If the Google API is temporarily overloaded, Dracula will smoothly 
# retry the connection in the background before raising an exception.
response = ai.chat("What is the meaning of life?")

📊 SQLite Usage Stats

Dracula automatically tracks how many messages you've sent and received, and how many characters were exchanged in total. These stats are safely written to a local database (~/.dracula/stats.db), so they accumulate securely over time without risking data corruption.

print(ai.get_stats())
# {
#   "total_messages": 5,
#   "total_responses": 5,
#   "total_characters_sent": 120,
#   "total_characters_received": 3400
# }

ai.reset_stats()

📝 Logging

Dracula has a built-in logging system that you can turn on or off. By default logging is completely silent. When enabled, it shows detailed information about what Dracula is doing internally — useful for debugging. You can also save logs to a file with automatic log rotation, which means when the log file gets too big, a new one is created automatically.

# Enable terminal logging
ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"), logging=True)

# Enable logging with a specific level
ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"), logging=True, log_level="WARNING")

# Enable file logging with rotation
ai = Dracula(
    api_key=os.getenv("GEMINI_API_KEY"),
    logging=True,
    log_file="dracula.log",
    log_max_bytes=1 * 1024 * 1024,  # 1MB per file
    log_backup_count=3               # keep 3 backups
)

# Change log level anytime
ai.set_log_level("ERROR")

🔗 Chainable Methods

Instead of calling each setter method on a separate line, chainable methods let you combine multiple settings into a single, clean line of code.

ai.set_prompt("You are a chef.").set_temperature(0.9).set_language("Turkish")

🧹 Context Manager

A context manager lets you use Dracula with Python's with statement. The benefit is automatic cleanup — when the with block ends, Dracula automatically clears the memory and resets the stats.

with Dracula(api_key=os.getenv("GEMINI_API_KEY")) as ai:
    ai.chat("Hello!")
    ai.print_history()
# Memory and stats automatically reset here ✅

🎭 Role Playing Mode

Dracula comes with a set of built-in personas that you can switch between instantly. Each persona has its own predefined prompt, temperature, and language settings.

print(ai.list_personas())
# ['assistant', 'pirate', 'chef', 'shakespeare', 'scientist', 'comedian']

ai.set_persona("pirate")
print(ai.chat("Hello, who are you?"))
# Arrr, I be a fearsome pirate! 🏴‍☠️

🖥️ Desktop Chat UI

Dracula comes with a ready-made PyQt6 desktop chat UI that you can use in your Windows apps. It supports dark and light themes, markdown rendering, and syntax highlighting for code blocks. (Requires UI installation: pip install dracula-ai[ui])

from dracula import Dracula, launch

ai = Dracula(api_key=os.getenv("GEMINI_API_KEY"))

launch(ai, title="My AI App", theme="dark")   # Dark theme
launch(ai, title="My AI App", theme="light")  # Light theme

🖥️ CLI Tool

Dracula comes with a built-in CLI tool that lets you chat with Gemini directly from the terminal, or launch the desktop UI, without writing any code.

dracula chat "Hello!"
dracula chat "Tell me a joke" --persona comedian
dracula chat "Merhaba" --language Turkish --stream
dracula ui  # Launches the PyQt6 Desktop App
dracula list-personas
dracula stats
dracula --version

⚡ Async Support with AsyncDracula

For async applications like Discord bots, FastAPI, and Telegram bots, use AsyncDracula instead of Dracula. It has all the same features but with full, non-blocking async support backed by aiosqlite.

import asyncio
from dracula import AsyncDracula, tool

@tool(description="Get the weather for a city")
async def get_weather(city: str) -> str:
    return f"25°C and sunny in {city}"

async def main():
    async with AsyncDracula(
        api_key=os.getenv("GEMINI_API_KEY"),
        tools=[get_weather]
    ) as ai:
        response = await ai.chat("What's the weather in Istanbul?")
        print(response)

        async for chunk in ai.stream("Tell me a story."):
            print(chunk, end="", flush=True)

asyncio.run(main())

🤖 Discord Bot Example

Thanks to async support, building an AI-powered Discord bot with Dracula takes just a few lines of code.

import discord
from discord.ext import commands
from dracula import AsyncDracula, tool
from dotenv import load_dotenv
import os

load_dotenv()

bot = commands.Bot(command_prefix="!", intents=discord.Intents.all())
ai = AsyncDracula(api_key=os.getenv("GEMINI_API_KEY"))

@bot.command()
async def chat(ctx, *, message: str):
    response = await ai.chat(message)
    await ctx.send(response)

bot.run(os.getenv("DISCORD_TOKEN"))

🌐 FastAPI Example

Dracula works perfectly with FastAPI thanks to async support.

from fastapi import FastAPI
from dracula import AsyncDracula
from dotenv import load_dotenv
import os

load_dotenv()

app = FastAPI()
ai = AsyncDracula(api_key=os.getenv("GEMINI_API_KEY"))

@app.get("/chat")
async def chat(message: str):
    response = await ai.chat(message)
    return {"response": response}

@app.get("/stream")
async def stream(message: str):
    from fastapi.responses import StreamingResponse
    return StreamingResponse(
        ai.stream(message),
        media_type="text/plain"
    )

Error Handling

Dracula provides custom exceptions so you can handle different types of errors separately and give your users clear, meaningful error messages.

from dracula import (
    ValidationException,
    ChatException,
    InvalidAPIKeyException,
    PersonaException,
    ToolException
)

try:
    ai = Dracula(api_key="", temperature=5.0)
except ValidationException as e:
    print(f"Validation error: {e}")
except InvalidAPIKeyException as e:
    print(f"API key error: {e}")
except ChatException as e:
    print(f"Chat error: {e}")
except PersonaException as e:
    print(f"Persona error: {e}")
except ToolException as e:
    print(f"Tool error: {e}")

Known Issues

AsyncDracula — aiohttp connector warning

When using AsyncDracula, you may see this warning after your program ends:

Exception ignored in: <function BaseApiClient._get_aiohttp_session...>
AttributeError: 'NoneType' object has no attribute 'from_iterable'

This is a known bug in the google-genai library and is not caused by Dracula. It does not affect functionality in any way.

Getting Your API Key

  1. Go to https://aistudio.google.com
  2. Sign in with your Google account
  3. Click "Get API Key"
  4. Store it safely in a .env file:
GEMINI_API_KEY=your-api-key

License

MIT License — feel free to use this in your own projects!

Author

Suleyman Ibis

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dracula_ai-0.8.0.tar.gz (35.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dracula_ai-0.8.0-py3-none-any.whl (37.1 kB view details)

Uploaded Python 3

File details

Details for the file dracula_ai-0.8.0.tar.gz.

File metadata

  • Download URL: dracula_ai-0.8.0.tar.gz
  • Upload date:
  • Size: 35.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for dracula_ai-0.8.0.tar.gz
Algorithm Hash digest
SHA256 a75271da3f7b8b39d60687e29995acd26cd11241884dff2b20031647e263e173
MD5 a80c10afbb8f0291baae7b0867dea43b
BLAKE2b-256 12ba9374b5c322749ec2dfc65f1a0cb22118935e24476cb684ec0906e3a9462a

See more details on using hashes here.

File details

Details for the file dracula_ai-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: dracula_ai-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 37.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for dracula_ai-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 80daeb1d029ed46bb2c69a7d158c18b31de484fff64faa0a5fd8f35dd6099eb6
MD5 46acbad072434d3e1ff05fd218d829cc
BLAKE2b-256 47e09cd3e07b1a2d293d4872864b62cf2934cc3b5fb50ea2f1972f806e1cf81e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page