core: Developer Tools for LLMs
The core module provides foundational functionality used by other modules.
We recommend reading the other quickstart sections before this one.
Metadata
The Metadata class provides a type-safe, dictionary-like interface for managing metadata in sessions and messages. It supports all common dictionary operations while ensuring type safety and proper data handling.
from prompttrail.core import Metadata, Session, Message
# Creating metadata
metadata = Metadata()
metadata["key"] = "value"
metadata.update({"another_key": 42})
# Using with Session
session = Session(metadata={"user_id": "123"})
session.metadata["language"] = "ja"
# Using with Message
message = Message(
content="Hello",
role="user",
metadata={"timestamp": "2024-01-26"}
)
Key features of Metadata:
Dictionary-like operations (get, set, update)
Support for complex value types (strings, numbers, lists, dictionaries)
Copy operations that maintain independence
Type safety and proper data handling
Cache
LLM APIs are sometimes expensive. PromptTrail has a built-in cache mechanism to reduce the number of API calls.
Cache is implemented as a CacheProvider. You can pass a CacheProvider to the model’s configuration.
Let’s see an example:
from prompttrail.core.cache import LRUCacheProvider
from prompttrail.models.openai import OpenAIConfig, OpenAIModel
config = OpenAIConfig(
api_key=api_key,
model_name="gpt-4o-mini",
max_tokens=100,
# Just pass a cache provider to the configuration
cache_provider=LRUCacheProvider()
)
model = OpenAIModel(config)
We passed a LRUCacheProvider to the configuration. And the configuration is passed to the model.
Now, the model will cache the messages.
Just call send method as usual. If you pass the same session, you will get the cached message.
from prompttrail.core import Session, Message
session = Session(
messages = [Message(content="Hello, I'm a human.", role="user")]
)
# This time, the model calls the API
message_1 = model.send(session=session)
# This time, the model returns the cached message
message_2 = model.send(session=session)
Internally, CacheProvider has two methods:
add(session: Session, message: Message): add a new session and message pair to the cachesearch(session: Session) -> Message: get a message from the cache
Therefore, CacheProvider is basically a simple key-value store.
You don’t need to implement CacheProvider by yourself. PromptTrail has a built-in LRUCacheProvider which is a simple LRU cache. If you want a custom implementation, you can inherit from CacheProvider and implement the methods.
Mock
Say, you successfully build something using LLM. But, how can you test it?
One way is to call the LLM API, of course. But, it may be costly, slow, and even non-deterministic (e.g., GPT-3.5 and 4 is non-deterministic even if temperature is set to 0).
You may want to mock the LLM API. PromptTrail has a built-in mock mechanism.
Mock is implemented as a MockProvider. You can pass a MockProvider to the model’s configuration like CacheProvider.
Let’s see an example of OneTurnConversationMockProvider, which returns a message based on the last message of the session:
from prompttrail.core import Message
from prompttrail.core.mock import OneTurnConversationMockProvider
from prompttrail.models.openai import OpenAIConfig, OpenAIModel
# First, you need to define a table to define how the mock return response based on last message
role = "assistant"
conversation_table = {
"Hello": Message(content="Hi", role=role),
}
# Then, you can pass the table to the mock provider and pass the mock provider to the configuration
config = OpenAIConfig(
api_key="dummy", # API key is not used when using mock provider
model_name="gpt-4o-mini",
max_tokens=100,
mock_provider=OneTurnConversationMockProvider(conversation_table)
)
model = OpenAIModel(config)
Let’s call the model:
from prompttrail.core import Session, Message
session = Session(
messages = [Message(content="Hello", role="user")]
)
message = model.send(session=session)
assert message.content == "Hi"
As the session’s last message is “Hello”, the mock provider returns “Hi” based on the table.
There’re other mock providers:
EchoMockProvider: returns the same message as the last message of the sessionFunctionMockProvider: returns a message based on a function you defined
The usage of MockProvider is very similar to CacheProvider. You can pass a MockProvider to the model’s configuration like CacheProvider.
Actually, the role of MockProvider is very similar to that of CacheProvider. Because both return a Message object based on Session object. There’re two differences:
CacheProviderchecks the configuration because LLM may return different messages for the same session with different parameters.CacheProvidercan returnNoneif the message is not in the cache.MockProvideralways returns aMessageobject.
Note
MockProvider and CacheProvider cannot be used at the same time.
Debug and Logging
PromptTrail provides comprehensive debugging and logging capabilities to help you understand and troubleshoot your LLM applications.
Debug Mode
Debug mode can be enabled at both the runner and session level:
# Enable debug mode when running
session = runner.run(debug_mode=True)
# Or when creating a session
session = Session(debug_mode=True)
When debug mode is enabled, you’ll see detailed information about:
Template rendering process
Message generation and transformation
Tool execution and results
Cache hits and misses
API calls and responses
Logging
PromptTrail uses Python’s standard logging module. You can configure logging to see different levels of detail:
import logging
# Set logging level for all PromptTrail components
logging.basicConfig(level=logging.DEBUG)
# Or configure specific loggers
logger = logging.getLogger("prompttrail")
logger.setLevel(logging.INFO)
# Add handlers for custom output
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)
Common logging areas include:
Model interactions (
prompttrail.models)Runner execution (
prompttrail.agent.runners)Template processing (
prompttrail.agent.templates)Tool execution (
prompttrail.agent.tools)Cache operations (
prompttrail.core.cache)
Debuggable Base Class
All major components in PromptTrail inherit from the Debuggable base class, which provides consistent debugging capabilities:
from prompttrail.core.utils import Debuggable
class MyComponent(Debuggable):
def my_method(self):
self.debug("Debug message")
self.info("Info message")
self.warning("Warning message")
self.error("Error message")
This ensures consistent logging behavior across the library and makes it easier to add debugging to your own components.