Kindred AI SDK - A Python SDK that intercepts HTTP requests and reroutes them based on configuration rules
Project description
Kindred AI SDK
A Python SDK that intercepts HTTP requests and reroutes them based on configuration rules. Works with any domain or API provider - not limited to any specific service. Perfect for rerouting LLM calls to caching gateways, mocking payment APIs during development, or centralized logging of third-party API usage.
Features
- 🎯 Intercepts outgoing HTTP requests from
requestsandhttpxlibraries - 🔀 Reroutes requests to custom destinations based on hostname rules
- 🔒 Preserves all headers, query parameters, and request bodies
- 🚀 Supports both sync (
requests,httpx.Client) and async (httpx.AsyncClient) clients - 🎛️ Context manager support for temporary interception
- 📝 Verbose logging option for debugging
Installation
From Local Directory (Development)
cd sidecar/SDK
pip install -e ".[all]"
From PyPI (When Published)
pip install kindred-ai[all]
Quick Setup (3 Steps)
-
Set environment variables:
# Kindred URL (used to fetch restricted URLs and as the routing destination) export KINDRED_URL="http://your-server.com:8080" # Agent token (also serves as agent ID, provided by Kindred) export KINDRED_AGENT_TOKEN="your-token-here"
-
Add 2 lines to your code:
from kindred_ai import Interceptor Interceptor.init(verbose=True)
-
That's it! HTTP requests whose URLs match your restricted list are now automatically routed through Kindred.
See USER_GUIDE.md for detailed instructions.
Quick Start
Automatic restricted-URL mode (recommended)
Let Kindred manage which URLs are intercepted based on the restricted URLs configured for your agent:
export KINDRED_URL="http://your-server.com:8080"
export KINDRED_AGENT_TOKEN="your-token-here"
from kindred_ai import Interceptor
# Initialize without explicit rules - Kindred_URL + KINDRED_AGENT_TOKEN
# are used to fetch restricted URLs for this agent.
Interceptor.init(verbose=True)
import requests
# Only requests whose URLs are in the restricted list will be intercepted.
response = requests.get("https://api.stripe.com/v1/charges")
print(response.json())
# If there are no restricted URLs, nothing is intercepted.
Basic Usage with Specific Rules
from kindred_ai import Interceptor
# Define routing rules - works with ANY domain/provider
rules = {
"api.openai.com": "https://my-custom-gateway.com/openai",
"api.stripe.com": "http://localhost:8080/stripe-mock",
"api.github.com": "https://proxy.example.com/github"
}
# Initialize the interceptor
Interceptor.init(routing_rules=rules, verbose=True)
# Now all requests matching the rules will be rerouted
import requests
response = requests.get("https://api.stripe.com/v1/charges")
# Actually goes to: http://localhost:8080/stripe-mock/v1/charges
# Stop interception
Interceptor.stop()
Catch-All with Code
You can also specify a catch-all endpoint directly in code:
from kindred_ai import Interceptor
# Route ALL requests to a single endpoint
rules = {
"*": "http://localhost:8080/proxy" # "*" matches all hostnames
}
Interceptor.init(routing_rules=rules, verbose=True)
# All API requests now go to localhost:8080/proxy
Using with Context Manager
from kindred_ai import Interceptor
rules = {
"api.stripe.com": "http://localhost:8080/stripe-mock"
}
# Temporary interception (useful for testing)
with Interceptor.scope(routing_rules=rules, verbose=True):
import requests
response = requests.post("https://api.stripe.com/v1/charges", json={...})
# Request is intercepted and rerouted to localhost:8080
# Outside the context, requests are normal
response = requests.post("https://api.stripe.com/v1/charges", json={...})
# Goes to the original Stripe API
Example: Rerouting LLM API Calls
from kindred_ai import Interceptor
from openai import OpenAI
# Reroute OpenAI API calls to your caching gateway
rules = {
"api.openai.com": "https://my-cache-gateway.com/openai"
}
Interceptor.init(routing_rules=rules, verbose=True)
client = OpenAI(api_key="sk-...")
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Hello!"}]
)
# Request is automatically rerouted to your gateway
Interceptor.stop()
Example: Mocking Payment APIs
from kindred_ai import Interceptor
# Reroute Stripe calls to a mock server during development
rules = {
"api.stripe.com": "http://localhost:8080/stripe-mock"
}
Interceptor.init(routing_rules=rules, verbose=True)
# All Stripe API calls now go to your mock server
API Reference
Interceptor.init(routing_rules=None, agent_token=None, kindred_url=None, verbose=False)
Initialize the interceptor.
Parameters:
routing_rules(dict, optional): Dictionary mapping original hostnames to new destinations.- If provided, these rules are used directly (you can still use
"*"/"__all__"for catch‑all behavior). - Example:
{"api.stripe.com": "http://localhost:8080/stripe-mock", "api.openai.com": "https://my-gateway.com/openai"}
- If provided, these rules are used directly (you can still use
agent_token(str, optional): Agent token (also serves as agent ID). If not provided,KINDRED_AGENT_TOKENenvironment variable is used.kindred_url(str, optional): Base Kindred URL. If not provided,KINDRED_URLenvironment variable is used.verbose(bool): Whether to log intercepted requests (default: False)
Behavior when routing_rules is None:
- If
agent_token/kindred_url(or the corresponding env vars) are set, the SDK:- Fetches restricted URLs from:
KINDRED_URL/restricted-urls?agent_id=<KINDRED_AGENT_TOKEN> - Builds routing rules from those URLs (one rule per hostname pointing to
KINDRED_URL) - Initializes interception only if at least one restricted URL is found
- Fetches restricted URLs from:
- If the fetch fails or no restricted URLs are returned, the interceptor is not initialized (no interception).
Raises:
ValueError: If routing rules are invalid or neither rules nor the required environment variables are provided
Environment Variables:
KINDRED_URL: Base Kindred URL, used both to fetch restricted URLs and as the routing destination.KINDRED_AGENT_TOKEN: Token used as agent ID when fetching restricted URLs, and automatically added asX-Kindred-Agent-Tokenheader to intercepted requests.
Interceptor.stop()
Stop interception and restore original behavior.
Interceptor.scope(routing_rules, verbose=False)
Context manager for temporary interception.
Parameters:
routing_rules(dict): Dictionary mapping original hostnames to new destinationsverbose(bool): Whether to log intercepted requests
Example:
with Interceptor.scope({"api.stripe.com": "http://localhost:8080"}):
# Requests here are intercepted
pass
# Requests here are normal
Interceptor.is_active()
Check if the interceptor is currently active.
Returns:
bool: True if interceptor is initialized, False otherwise
Interceptor.get_rules()
Get the current routing rules.
Returns:
dict: Copy of the current routing rules dictionary
Routing Rules
Routing rules map original hostnames to new destinations:
rules = {
"api.openai.com": "https://my-gateway.com/openai",
"api.stripe.com": "http://localhost:8080/stripe-mock"
}
Catch-All Routing (advanced, manual rules):
You can route ALL requests to a single endpoint using a catch‑all key in routing_rules:
rules = {
"*": "http://localhost:8080/proxy" # Routes all requests
}
Interceptor.init(routing_rules=rules)
Important Notes:
- The destination must include a hostname (e.g.,
https://example.com, not/relative/path) - Paths from the original URL are preserved and appended to the destination path
- Query parameters and fragments are preserved
- Headers (including Authorization) are forwarded to the new destination
- If
KINDRED_AGENT_TOKENenvironment variable is set, it's automatically added asX-Kindred-Agent-Tokenheader to all intercepted requests - Original destination URL is added as
X-Kindred-Original-URLheader - Original payload/body is added as
X-Kindred-Original-Payloadheader (with type inX-Kindred-Original-Payload-Type) - Specific hostname rules take precedence over catch-all rules
Example URL Rewriting:
-
Original:
https://api.stripe.com/v1/charges?limit=10 -
Rule:
{"api.stripe.com": "http://localhost:8080/stripe-mock"} -
Result:
http://localhost:8080/stripe-mock/v1/charges?limit=10 -
Original:
https://api.openai.com/v1/chat/completions?model=gpt-4 -
Rule:
{"api.openai.com": "https://my-gateway.com/openai"} -
Result:
https://my-gateway.com/openai/v1/chat/completions?model=gpt-4
Supported Libraries
- ✅
requests- Full support forrequests.Session.request - ✅
httpx- Full support for bothhttpx.Client.request(sync) andhttpx.AsyncClient.request(async)
SSL/TLS Considerations
When rerouting traffic from HTTPS to HTTPS, ensure your gateway has valid SSL certificates. If you're using self-signed certificates for local development, you may need to configure the underlying HTTP library to disable SSL verification (not recommended for production):
import requests
import urllib3
# Disable SSL warnings (not recommended for production)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Use verify=False in requests (not recommended for production)
response = requests.get("https://api.example.com/endpoint", verify=False)
Request Headers Added by SDK
The SDK automatically adds several headers to intercepted requests to provide context to your server:
Agent Token Header
If KINDRED_AGENT_TOKEN environment variable is set, it's added as X-Kindred-Agent-Token header. This allows your server to identify which agent/user is making requests.
Setup:
export KINDRED_AGENT_TOKEN="your-token-here"
Original Destination Header
The original destination URL (before rerouting) is always added as X-Kindred-Original-URL header. This tells your server where the request was originally supposed to go.
Example:
- Original request:
https://api.stripe.com/v1/charges - Rerouted to:
http://your-server.com:8080/proxy/v1/charges - Header added:
X-Kindred-Original-URL: https://api.stripe.com/v1/charges
Original Payload Headers
The original request payload/body is captured and added as headers:
X-Kindred-Original-Payload: The payload contentX-Kindred-Original-Payload-Type: The type (json,string, orbase64)
Payload Types:
- JSON: If payload is a dict/list, it's serialized as JSON string
- String: If payload is a string, it's included as-is
- Base64: If payload is bytes, it's base64-encoded
Example:
from kindred_ai import Interceptor
Interceptor.init(verbose=True)
import requests
response = requests.post(
"https://api.stripe.com/v1/charges",
json={"amount": 1000, "currency": "usd"}
)
# Headers added:
# X-Kindred-Original-URL: https://api.stripe.com/v1/charges
# X-Kindred-Original-Payload: {"amount": 1000, "currency": "usd"}
# X-Kindred-Original-Payload-Type: json
# X-Kindred-Agent-Token: your-token-here (if set)
Note: Headers are only added to requests that are intercepted (rerouted). Non-intercepted requests are unchanged.
Error Handling
The SDK is designed to fail safely:
- If a library (
requestsorhttpx) is not available, the patcher will skip it with a warning - If URL parsing fails, the original URL is returned unchanged
- Invalid routing rules raise
ValueErrorduring initialization - If
KINDRED_AGENT_TOKENis not set, requests still work (just without the token header)
Development
Running Tests
# Install test dependencies
pip install -e ".[all]"
# Run tests
python -m pytest tests/
# or
python -m unittest discover tests
Project Structure
kindred_ai/
├── __init__.py # Public API
├── core.py # Main interceptor logic
├── rules.py # URL rewriting logic
├── patchers/
│ ├── __init__.py
│ ├── requests_patch.py # requests library patcher
│ └── httpx_patch.py # httpx library patcher
└── tests/
├── test_requests.py
└── test_integration.py
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Project details
Release history Release notifications | RSS feed
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 kindred_ai-0.1.0.tar.gz.
File metadata
- Download URL: kindred_ai-0.1.0.tar.gz
- Upload date:
- Size: 21.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bf6c0a214e949a466e34efd653c38cde83ab629c6642eae9a745a96a90f9fe6d
|
|
| MD5 |
beaae72e94ce7460bd8396b2b5ae4283
|
|
| BLAKE2b-256 |
810ccd2213f1f7f29129edf9d69f65fd713a79ea90a0b781eda517c065abd092
|
File details
Details for the file kindred_ai-0.1.0-py3-none-any.whl.
File metadata
- Download URL: kindred_ai-0.1.0-py3-none-any.whl
- Upload date:
- Size: 18.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bebbeeb627c22bb55f4e55145b96d58de3cb28d04de2b1ce099f364f06b9e92e
|
|
| MD5 |
2ac894938f21ab5d67a4669fdd3ac6b3
|
|
| BLAKE2b-256 |
c04da379643bd929827df3b28ff8be0cb31a22f2ef7bab8dc6975c2637bbc67f
|