Metadata-Version: 2.4
Name: sentiz-python-sdk
Version: 0.1.0
Summary: Add your description here
Author-email: Sentiz <sdk@sentiz.io>
Project-URL: Homepage, https://sentiz.io
Project-URL: Documentation, https://docs.sentiz.io
Project-URL: Repository, https://github.com/sentiz/sentiz-python-sdk
Project-URL: Bug Tracker, https://github.com/sentiz/sentiz-python-sdk/issues
Keywords: ai,agents,observability,llm,tracing,monitoring
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.13
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.24.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: backoff>=2.2.1
Provides-Extra: openai
Requires-Dist: openai>=1.0.0; extra == "openai"
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.20.0; extra == "anthropic"
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
Provides-Extra: all
Requires-Dist: openai>=1.0.0; extra == "all"
Requires-Dist: anthropic>=0.20.0; extra == "all"
Requires-Dist: langchain-core>=0.1.0; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: mypy>=1.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=4.0; extra == "dev"

**Module breakdown**


**File** -> **What it does**
pyproject.toml --> PyPI-ready packaging with optional extras: pip install sentiz[openai], [anthropic], [langchain], [all]
_version.py --> Single version source — reflected in User-Agent headers and every span envelope
models.py --> Pydantic v2 data models: Span, Session, InteractionEvent, DecisionEvent, EventEnvelope
config.py --> SentizConfig dataclass with env-var fallbacks for every field — zero code changes needed for containerised deploys
masking.py --> PiiMasker — built-in patterns for credit cards, SSNs, API keys, phones; field-name redaction; custom regex; deep recursive on dicts/lists
costs.py --> 30+ models priced (GPT-4o, Claude Opus 4, Gemini 2.5, Llama, Mistral); register_model_pricing() for custom models
context.py --> ContextVar-based session/span propagation — works correctly in both asyncio and threads (threading.local would break asyncio)
utils.py --> Stack-walking source location; git SHA from 8 CI env vars with subprocess fallback; safe JSON repr
transport/http.py --> httpx-based sync + async HTTP client; custom JSON encoder for datetimes/UUIDs/Pydantic models
transport/consumer.py --> Daemon thread (modelled on Langfuse's pattern); size + time dual flush triggers; exponential backoff with backoff; drops on 4xx, retries on 5xx; sentinel-based graceful shutdown
transport/queue.py --> QueueManager — Queue(maxsize=) drop-on-full; atexit hook for process-exit drain; flush() / shutdown()
client.py --> SentizClient (sync) + AsyncSentizClient (async, wraps sync via asyncio.to_thread); all capture methods; update_batch_settings() at runtime
decorators.py --> @session, @trace — both auto-detect sync vs async at decoration time
integrations/openai.py --> Patches Completions.create and AsyncCompletions.create; handles streaming wrappers that capture token counts post-stream
integrations/anthropic.py --> Patches Messages.create and AsyncMessages.create
integrations/langchain/handler.py --> SentizCallbackHandler(BaseCallbackHandler) — captures chain, LLM, tool, and agent-decision events; auto-manages session lifecycle



Three key design decisions:

**Daemon thread + ContextVar** — the consumer thread won't block process exit, and ContextVar means concurrent async sessions never bleed into each other. Both are patterns copied from production-proven SDKs.
**Fail-silent everywhere** — every external path (enqueue, HTTP, instrumentation patch, before_send hook) is wrapped in try/except. The SDK's contract: it will never raise into the host application.
**enabled=False is a true no-op** — when disabled, no thread is started, no queue is created, all capture calls return immediately. Safe to leave instrumentation in production code and toggle off via SENTIZ_ENABLED=false.


Root level — just pyproject.toml. Everything else lives inside the sentiz/ package.
sentiz/ — five layers:
__init__.py + client.py are the public surface. Everything else is an implementation detail. Users only ever touch what's in __init__.py.
config.py / models.py / context.py are the data layer — configuration, Pydantic wire-format models, and ContextVar-based session propagation.
masking.py / costs.py / utils.py are standalone utilities — no cross-dependencies, easy to unit test in isolation.
transport/ is the reliability layer — queue.py owns the in-memory buffer and atexit hook, consumer.py is the background daemon thread that drains it with retry, http.py is the thin httpx wrapper that does the actual POST.
integrations/ is the zero-code-change layer — openai.py and anthropic.py monkey-patch those SDKs on init, langchain/handler.py implements BaseCallbackHandler for chain/LLM/tool/agent events.
Dependency direction: integrations → client → transport → http. The masking, costs, context, and utils modules are leaves with no upward dependencies — any layer can use them without creating cycles.integrations/mcp/__init__.py --> Clean exports: SentizMcpClientMiddleware, instrument_mcp_server, mcp_tool, patch_mcp_client
integrations/mcp/client.py --> SentizMcpClientMiddleware wraps mcp.ClientSession — traces every call_tool() as a SpanKind.MCP span + McpCallEvent; captures server name/version from initialize(); patch_mcp_client() for global auto-patch
integrations/mcp/server.py --> instrument_mcp_server() patches FastMCP (_tool_manager) and low-level mcp.Server; traces every tool execution as SpanKind.MCP_SERVER span
integrations/mcp/decorators.py --> @mcp_tool per-function decorator; auto-detects sync vs async; captures args (PII-masked), result, and errors


**v0.2.0 additions:**
- MCP instrumentation: SpanKind.MCP + SpanKind.MCP_SERVER added to models; McpTransport enum (stdio/sse/http); McpCallEvent dedicated wire type
- client.capture_mcp_call() — explicit API consistent with capture_tool_call / capture_llm_call
- sentiz.capture_mcp_call() — global proxy
- Install with: pip install "sentiz[mcp]"


To publish to Artifactory:
--------------------------
# One-time setup
cp .pypirc.template ~/.pypirc
# Edit ~/.pypirc with your Artifactory URL and credentials

# Build and publish
make publish-artifactory

# Or in CI/CD (no ~/.pypirc needed)
TWINE_REPOSITORY_URL=https://company.jfrog.io/artifactory/api/pypi/sentiz/
TWINE_USERNAME=ci-user
TWINE_PASSWORD=api-key
make publish-ci


Filling Template Files:
-----------------------
 A few notes on filling these in:
.pypirc.template — copy to ~/.pypirc (never commit the filled-in version to git). Replace:

YOUR-COMPANY — your JFrog subdomain (e.g. acme)
YOUR-PYPI-REPO — the name of your PyPI virtual or local repo in Artifactory (e.g. sentiz-pypi)
YOUR-ARTIFACTORY-USERNAME — your Artifactory username or service account
YOUR-ARTIFACTORY-API-KEY-OR-TOKEN — your Artifactory API key or identity token (found under your Artifactory profile → API Key)

pip.conf.template — copy to ~/.config/pip/pip.conf on Linux/Mac, or %APPDATA%\pip\pip.ini on Windows. 
The extra-index-url = https://pypi.org/simple/ line is important — it means pip falls back to public PyPI for packages not in your Artifactory repo (like httpx, pydantic, etc.). 
Without it, installing sentiz[openai] would fail to resolve openai from Artifactory unless you also mirror it there.
