Minimal, extensible LLM observability SDK with OpenAI and Gemini support.
Project description
aiobs
A tiny, extensible observability layer for LLM calls. Add three lines around your code and get JSON traces for requests, responses, timings, and errors.
Supported Providers
- OpenAI — Chat Completions API (
openai>=1.0) - Google Gemini — Generate Content API (
google-genai>=1.0)
Quick Install
# Core only
pip install aiobs
# With OpenAI support
pip install aiobs[openai]
# With Gemini support
pip install aiobs[gemini]
# With all providers
pip install aiobs[all]
Quick Start
from aiobs import observer
observer.observe() # start a session and auto-instrument providers
# ... make your LLM calls (OpenAI, Gemini, etc.) ...
observer.end() # end the session
observer.flush() # write a single JSON file to disk
By default, events flush to ./llm_observability.json. Override with LLM_OBS_OUT=/path/to/file.json.
Provider Examples
OpenAI
from aiobs import observer
from openai import OpenAI
observer.observe()
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Hello!"}]
)
observer.end()
observer.flush()
Google Gemini
from aiobs import observer
from google import genai
observer.observe()
client = genai.Client()
response = client.models.generate_content(
model="gemini-2.0-flash",
contents="Hello!"
)
observer.end()
observer.flush()
Function Tracing with @observe
Trace any function (sync or async) by decorating it with @observe:
from aiobs import observer, observe
@observe
def research(query: str) -> list:
# your logic here
return results
@observe(name="custom_name")
async def fetch_data(url: str) -> dict:
# async logic here
return data
observer.observe(session_name="my-pipeline")
research("What is an API?")
observer.end()
observer.flush()
Decorator Options
| Option | Default | Description |
|---|---|---|
name |
function name | Custom display name for the traced function |
capture_args |
True |
Whether to capture function arguments |
capture_result |
True |
Whether to capture the return value |
# Don't capture sensitive arguments
@observe(capture_args=False)
def login(username: str, password: str):
...
# Don't capture large return values
@observe(capture_result=False)
def get_large_dataset():
...
What Gets Captured
For each decorated function call:
- Function name and module
- Input arguments (args/kwargs)
- Return value
- Timing: start/end timestamps,
duration_ms - Errors: exception name and message if the call fails
- Callsite: file path, line number where the function was defined
Run the Examples
-
Simple OpenAI example:
python example/simple-chat-completion/chat.py -
Gemini example:
python example/gemini/main.py -
Multi-file pipeline example:
python -m example.pipeline.main "Explain vector databases to a backend engineer"
What Gets Captured (LLM Calls)
- Provider:
openaiorgemini - API: e.g.,
chat.completionsormodels.generateContent - Request: model, messages/contents, core parameters
- Response: text, model, token usage (when available)
- Timing: start/end timestamps,
duration_ms - Errors: exception name and message if the call fails
- Callsite: file path, line number, and function name where the API was called
Data Models
Internally, the SDK structures data with Pydantic models (v2):
aiobs.Session– Session metadataaiobs.Event– LLM provider call eventaiobs.FunctionEvent– Decorated function trace eventaiobs.ObservedEvent(Event +session_id)aiobs.ObservedFunctionEvent(FunctionEvent +session_id)aiobs.ObservabilityExport(flush payload)
These are exported to allow downstream tooling to parse and validate the JSON output and to build integrations.
Extensibility
Providers are classes that implement a small abstract interface and install their own hooks.
- Base class:
aiobs.BaseProvider - Built-in:
OpenAIProvider,GeminiProvider(auto-detected and installed if available)
Custom provider skeleton:
from aiobs import BaseProvider, observer
class MyProvider(BaseProvider):
name = "my-provider"
@classmethod
def is_available(cls) -> bool:
try:
import my_sdk # noqa: F401
return True
except Exception:
return False
def install(self, collector):
# monkeypatch or add hooks into your SDK, then
# call collector._record_event({ ... normalized payload ... })
def unpatch():
pass
return unpatch
# Register before observe()
observer.register_provider(MyProvider())
observer.observe()
Architecture
- Core
Collectorholds sessions/events and flushes a single JSON file.aiobs.models.*define Pydantic schemas for sessions/events/export.
- Providers (N-layered)
providers/base.py:BaseProviderinterface.providers/openai/: OpenAI Chat Completions instrumentation.providers/gemini/: Google Gemini Generate Content instrumentation.
Providers construct Pydantic request/response models and pass typed Event objects to the collector; only the collector serializes to JSON.
Docs
Sphinx documentation lives under docs/.
- Install docs deps:
pip install aiobs[docs]
- Build HTML docs:
python -m sphinx -b html docs docs/_build/html
- Open
docs/_build/html/index.htmlin your browser.
GitHub Pages
- Docs auto-deploy from
mainvia GitHub Actions. - Site available at: https://neuralis-in.github.io/aiobs/
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aiobs-0.1.0.tar.gz.
File metadata
- Download URL: aiobs-0.1.0.tar.gz
- Upload date:
- Size: 96.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9fd9e4e861212faff8df0eb5743886c5734c86aaf4c8ec29f2deba38636ab709
|
|
| MD5 |
488c90e045d4a5713fc7bd925d7148cf
|
|
| BLAKE2b-256 |
8137c70f46975c00644110dda5f8d9808799628c7f81522f9a2944b2c2a7ffc1
|
File details
Details for the file aiobs-0.1.0-py3-none-any.whl.
File metadata
- Download URL: aiobs-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7049116d4c2a6659b66481580d1593639366d3cb50eabe3b203386c17be0b9a8
|
|
| MD5 |
98d10b99a4eef2708dd9dcf3c3213edc
|
|
| BLAKE2b-256 |
55d0b85d6ff0b966d17fd3ac5bae92a79a267889647805ab12883c34d5a5ce10
|