Metadata-Version: 2.4
Name: eldros-sdk
Version: 0.1.0
Summary: OpenTelemetry-native tracing SDK for Eldros AI agents
License: MIT
License-File: LICENSE
Keywords: opentelemetry,tracing,llm,ai-agents,observability
Author: Eldros
Author-email: shyam@sentient.xyz
Requires-Python: >=3.10,<4.0
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.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: System :: Monitoring
Provides-Extra: all
Provides-Extra: anthropic
Provides-Extra: claude-agent-sdk
Provides-Extra: openai
Requires-Dist: claude-agent-sdk (>=0.2.0) ; extra == "claude-agent-sdk" or extra == "all"
Requires-Dist: langsmith[claude-agent-sdk] (>=0.8.17) ; extra == "claude-agent-sdk" or extra == "all"
Requires-Dist: opentelemetry-api (>=1.25.0)
Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1.25.0)
Requires-Dist: opentelemetry-instrumentation-anthropic (>=0.25.0) ; extra == "anthropic" or extra == "all"
Requires-Dist: opentelemetry-instrumentation-openai (>=0.25.0) ; extra == "openai" or extra == "all"
Requires-Dist: opentelemetry-sdk (>=1.25.0)
Requires-Dist: pydantic (>=2.0)
Project-URL: Homepage, https://github.com/GShyam001/Construct
Project-URL: Repository, https://github.com/GShyam001/Construct
Description-Content-Type: text/markdown

# eldros-sdk

OpenTelemetry-native tracing SDK for Eldros AI agents. One `init()` call
resolves your Langfuse credentials from the Eldros platform, wires up
OpenTelemetry, and auto-instruments your LLM provider clients. Drop a `@trace`
decorator on your handler and you get a root span per call.

**You need exactly one thing: an Eldros API key** (`agt_...`). The SDK
resolves Langfuse credentials from the Eldros platform at startup — you never
configure Langfuse directly.

## Install

```bash
pip install eldros-sdk              # core
pip install "eldros-sdk[claude-agent-sdk]"    # + Claude Agent SDK instrumentation via LangSmith
pip install "eldros-sdk[openai]"    # + OpenAI auto-instrumentation
pip install "eldros-sdk[anthropic]" # + Anthropic auto-instrumentation
pip install "eldros-sdk[all]"       # everything
```

## Quick start

```bash
export ELDROS_API_KEY=agt_...
```

```python
from eldros_sdk import init, trace

init(agent_id="support-bot")   # api_key is read from ELDROS_API_KEY

@trace
async def handle_conversation(user_message: str) -> str:
    # OpenAI / Anthropic calls in here are auto-instrumented as child spans.
    ...
```

`init()` blocks briefly at startup to call the Eldros platform and fetch your
Langfuse credentials — call it before starting your event loop (e.g. at module
import time or in your `if __name__ == "__main__"` block).

## Configuration

Every argument can come from an environment variable. Explicit `init(...)` kwargs
win over env vars.

| `init()` arg             | Env var                  | Default                                        |
| ------------------------ | ------------------------ | ---------------------------------------------- |
| `api_key`                | `ELDROS_API_KEY`         | — *(required)*                                 |
| `api_base_url`           | `ELDROS_API_BASE_URL`    | — *(required until official domain is live)*   |
| `agent_id`               | `ELDROS_AGENT_ID`        | —                                              |
| `traffic_type` / `env`   | `ELDROS_TRAFFIC_TYPE`    | `prod`                                         |
| `service_name`           | `ELDROS_SERVICE_NAME`    | `eldros-agent`                                 |
| `instrument_claude_agent_sdk` | `ELDROS_INSTRUMENT_CLAUDE_AGENT_SDK` | `false` *(opt in explicitly)*  |
| `instrument_openai`      | —                        | `false` *(opt in explicitly)*                  |
| `instrument_anthropic`   | —                        | `false` *(opt in explicitly)*                  |
| `capture_content`        | `ELDROS_CAPTURE_CONTENT` | `true`                                         |
| `debug`                  | `ELDROS_TRACE_DEBUG`     | `false`                                        |
| `enabled`                | `ELDROS_TRACE_ENABLED`   | `true`                                         |

- **`enabled=False`** makes the whole SDK a no-op — safe to leave in any environment.
- **`debug=True`** (or `ELDROS_TRACE_DEBUG=true`) also prints spans to the console.
- **`capture_content=False`** strips prompt/response text from LLM spans.

## Decorator options

```python
@trace(name="checkout", capture_io=True, attributes={"tier": "pro"})
def run(payload: dict) -> dict:
    ...
```

`@trace` supports sync functions, coroutines, sync generators, and async
generators (the span stays open across the whole generation).

## Claude Agent SDK instrumentation

Install the `[claude-agent-sdk]` extra and pass the flag to `init()` — no separate call:

```bash
pip install "eldros-sdk[claude-agent-sdk]"
```

```python
import eldros_sdk

eldros_sdk.init(agent_id="my-agent", instrument_claude_agent_sdk=True)
```

`init()` is the single entry point. LangSmith patches `ClaudeSDKClient` and routes
spans through the global `TracerProvider` — alongside `@trace` and LLM spans.
Requires `ClaudeSDKClient` (not the module-level `query()`).

If `LANGSMITH_API_KEY` is also set, traces go to both your LangSmith account and
Eldros's Langfuse simultaneously (hybrid mode).

## Lifecycle

```python
from eldros_sdk import flush, shutdown

flush()      # force-export buffered spans (e.g. end of a serverless invocation)
shutdown()   # flush + tear down the provider
```

## Testing the SDK

```bash
# Verify credential resolution and SDK init
ELDROS_API_KEY=agt_... ELDROS_API_BASE_URL=https://... python3 -c "
import eldros_sdk
cfg = eldros_sdk.init(agent_id='test')
print('has_credentials:', cfg.has_credentials)
"

# Verify a span actually reaches Langfuse
ELDROS_API_KEY=agt_... ELDROS_TRACE_DEBUG=true python3 -c "
import eldros_sdk

eldros_sdk.init(agent_id='test', debug=True)

@eldros_sdk.trace
def hello():
    return 'world'

hello()
eldros_sdk.flush()
print('done — check Langfuse')
"
```

## Design notes

- **Credential resolution.** On `init()`, the SDK calls
  `GET {api_base_url}/api/v1/langfuse/resolve` with your `ELDROS_API_KEY` in the
  `X-Client-API-Key` header and receives `{public_key, secret_key, host}` for your
  org's Langfuse project. The Langfuse secret key is never stored on the client.
- **Langfuse via OTLP/HTTP.** No Langfuse SDK dependency — spans are exported as
  OTel protobuf over HTTP with basic auth.
- **Additive OTel.** If a `TracerProvider` already exists (Datadog, etc.) the SDK
  attaches its exporter to it instead of replacing it — spans flow to both destinations.
  **Call `init()` before any other tracing setup** so the SDK's provider is detected first.
- **Claude Agent SDK spans use the global provider.** LangSmith detects the global
  `TracerProvider` set by `init()` and routes all Claude Agent SDK OTel spans through it.
  This means any `SpanProcessor` you add to the global provider also receives those spans —
  you can route to your own Langfuse or any OTel backend alongside ours.
- **Auto-instrumentation is optional.** Provider instrumentors are extras; if a
  provider library isn't installed the SDK logs and continues.
- **Safe by default.** Spans are non-recording until `init()` runs, so `@trace`
  never breaks code even if `init()` is missing.

