logify360 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. logify360-1.0.0/.gitignore +16 -0
  2. logify360-1.0.0/CHANGELOG.md +182 -0
  3. logify360-1.0.0/PKG-INFO +548 -0
  4. logify360-1.0.0/README.md +481 -0
  5. logify360-1.0.0/logify360/__init__.py +99 -0
  6. logify360-1.0.0/logify360/_base_url.py +86 -0
  7. logify360-1.0.0/logify360/cli/__init__.py +33 -0
  8. logify360-1.0.0/logify360/cli/__main__.py +10 -0
  9. logify360-1.0.0/logify360/cli/code_patcher.py +449 -0
  10. logify360-1.0.0/logify360/cli/colors.py +65 -0
  11. logify360-1.0.0/logify360/cli/env_writer.py +100 -0
  12. logify360-1.0.0/logify360/cli/framework_detect.py +183 -0
  13. logify360-1.0.0/logify360/cli/installer.py +143 -0
  14. logify360-1.0.0/logify360/cli/key_info.py +83 -0
  15. logify360-1.0.0/logify360/cli/main.py +557 -0
  16. logify360-1.0.0/logify360/cli/prompts.py +146 -0
  17. logify360-1.0.0/logify360/client.py +205 -0
  18. logify360-1.0.0/logify360/evals.py +186 -0
  19. logify360-1.0.0/logify360/headers.py +95 -0
  20. logify360-1.0.0/logify360/integrations/__init__.py +60 -0
  21. logify360-1.0.0/logify360/integrations/_proxy_client.py +133 -0
  22. logify360-1.0.0/logify360/integrations/anthropic.py +171 -0
  23. logify360-1.0.0/logify360/integrations/gemini.py +180 -0
  24. logify360-1.0.0/logify360/integrations/langchain.py +450 -0
  25. logify360-1.0.0/logify360/integrations/llama_index.py +486 -0
  26. logify360-1.0.0/logify360/integrations/openai.py +158 -0
  27. logify360-1.0.0/logify360/middleware.py +262 -0
  28. logify360-1.0.0/logify360/observe.py +430 -0
  29. logify360-1.0.0/logify360/parsers.py +218 -0
  30. logify360-1.0.0/logify360/resilience.py +530 -0
  31. logify360-1.0.0/logify360/sampler.py +243 -0
  32. logify360-1.0.0/logify360/span.py +306 -0
  33. logify360-1.0.0/logify360/trace.py +222 -0
  34. logify360-1.0.0/logify360/transport.py +455 -0
  35. logify360-1.0.0/logify360/trust.py +310 -0
  36. logify360-1.0.0/logify360/types.py +66 -0
  37. logify360-1.0.0/pyproject.toml +142 -0
@@ -0,0 +1,16 @@
1
+ # Build artifacts
2
+ dist/
3
+ build/
4
+ *.egg-info/
5
+
6
+ # Virtualenv
7
+ .venv/
8
+ venv/
9
+ test-install/
10
+
11
+ # pytest / mypy / ruff caches
12
+ .pytest_cache/
13
+ .mypy_cache/
14
+ .ruff_cache/
15
+ __pycache__/
16
+ *.pyc
@@ -0,0 +1,182 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ Public PyPI release. Same API as 0.11.0.
6
+
7
+ ## 0.11.0
8
+
9
+ Trust layer helpers. Every trust feature stays off until an admin enables it per project.
10
+
11
+ ### Added
12
+
13
+ - **`warrant=`** on `create_openai`, `create_async_openai`, `create_anthropic` and `create_async_anthropic`, and a per-request **`with_warrant(token)`** helper. Sends `x-logify360-warrant`.
14
+ - **`sign_warrant_request(method, url, headers, private_key, public_jwk=None)`**: RFC 9421 sender binding (`sig1`, `ecdsa-p256-sha256`) plus the `x-logify360-warrant-key` header. Needs the new optional **`warrant`** extra (`cryptography>=42`, also in `all`).
15
+ - **`attenuate_warrant(...)`**: issues a narrower child through `POST /api/v1/warrants/:id/attenuate`. Needs a dashboard session today.
16
+ - **`is_decoy_tool_name(name)`** and **`handle_decoy_tool_calls(response)`** for OpenAI and Anthropic responses (dicts or SDK objects). Nothing is executed.
17
+ - **`mark_untrusted(span, source)`**: sets `logify360.untrusted` / `logify360.source` span metadata that survives the end-of-span PATCH.
18
+
19
+ ## 0.10.1
20
+
21
+ ### Fixed
22
+
23
+ - **Direct-provider fallback no longer forwards the proxy `Host` / `Content-Length`.** The fallback copied the request headers, so the key-bearing request could be addressed to Logify360's zone instead of the provider. Both are now stripped and recomputed for the provider URL (R10).
24
+ - **`resilience=` works with the openai/anthropic SDKs' `httpx2` clients** (the transport is built from the httpx flavor the provider SDK uses), and is now available on `create_anthropic()` / `create_async_anthropic()`, which attach the fallback key as `x-api-key`.
25
+ - `Fallback.api_key` is `repr=False`, so it cannot leak into a log line.
26
+
27
+ ## 0.10.0
28
+
29
+ **Breaking: Python 3.10 is no longer supported** (`requires-python >=3.11`). Python 3.10 reached end-of-life in October 2026, and the security releases of `pillow` (13.0.0+, pulled in by the `llama-index` extra) no longer install on it. Projects that must stay on 3.10 can keep using 0.9.0. No API changes.
30
+
31
+ ## 0.9.0
32
+
33
+ Opt-in proxy resilience (sync + async) and a dropped-span counter. Parity with the TypeScript SDK 0.18.0.
34
+
35
+ ### Added
36
+
37
+ - **`resilience=` on `create_openai()` / `create_async_openai()`** plus the building blocks `Resilience`, `Fallback`, `resilient_transport()`, `async_resilient_transport()` (and `ResilienceError`). Opt-in and default-safe. The resilient `httpx` transport:
38
+ - bounds the time to response headers (default 60 s - longer than the proxy's own 35 s upstream-header deadline) by overriding httpx's per-request read timeout, so a hang before headers no longer blocks to the 600 s default; a stream whose chunks keep flowing is never cut;
39
+ - retries **only** on `x-logify360-forwarded: false` (the provider was never reached, so no double-bill), honouring `Retry-After`, bounded and jittered. Connection errors and timeouts are not retried;
40
+ - optionally falls back to the provider directly with the **customer's own provider key** - on connection errors and `forwarded:false` responses, and on an ambiguous timeout only when `fallback_on_timeout=True`. `on_fallback` / `on_error` receive safe metadata only.
41
+ - forces the OpenAI SDK's own `max_retries` to 0 (its retry layer would re-add the forwarded:true double-bill) and warns once if the caller set `max_retries>0`.
42
+ - The provider key is never logged, sent to Logify360, or stored (R10).
43
+ - **`client.get_dropped_span_count()`** (IMP4-S1): a running count of ingest calls permanently dropped (an error status, a network/timeout failure, or an op submitted after `close()`). Trace spans are fire-and-forget, so a lost span is otherwise silent - poll this (e.g. before `close()`) to detect and alert on lost telemetry.
44
+
45
+ ## 0.8.2
46
+
47
+ Delivery-error and environment-parsing fixes. Two behaviour changes (see Fixed): `silent=False` now surfaces failures, and a key with whitespace or a control character inside it now raises `ValueError` when the client or a provider factory is built.
48
+
49
+ ### Fixed
50
+
51
+ - `silent=False` no longer swallows ingest failures. It used to make the background worker raise into a `Future` that `span.end()`, `trace.end()` and `start_trace()` discard, so a failed delivery was never seen by anyone. `close()` now re-raises the first failed delivery (and so does leaving a `with Logify360Client(...)` block), once and as the original exception. `close()` also shuts the client down: a trace or span used after it is dropped, not sent. It never replaces an exception the `with` block is already raising (that failure is logged at error level instead), and a process that never calls `close()` gets it logged at error level at exit. It cannot be raised at `span.end()`: ingest is fire-and-forget and returns before the request is sent. `silent=True`, the default, is unchanged: failures go to `on_error` and are never raised.
52
+ - `on_error` ran twice for every HTTP failure under `silent=False`. It now runs once per failed delivery in either mode.
53
+ - Padded API keys are stripped. A key read from a file or a secret mount carries a trailing newline; sent as is, httpx rejects the `Authorization` header (`Illegal header value b'Bearer sl_...\n'`, a message that quotes the key), so every provider call failed and, under `silent=True`, every trace was dropped without a word. `Logify360Client`, `Logify360Middleware` and `api_key=` / `LOGIFY360_API_KEY` in `create_openai()`, `create_async_openai()`, `create_anthropic()`, `create_async_anthropic()`, `create_gemini()` and `configure_gemini()` now strip surrounding whitespace. A blank key counts as unset: a blank `api_key=` falls through to `LOGIFY360_API_KEY`, and a blank environment variable is reported as missing.
54
+ - A key that still contains whitespace or a control character after the strip (an embedded newline, space, NUL, or any non-ASCII character) is now rejected with a `ValueError` when the client or provider factory is built: `[logify360] api_key contains whitespace or control characters`, or `LOGIFY360_API_KEY` when the key came from the environment. The message never quotes the key. Stripping cannot repair such a key, and httpx's error for it does quote the key: `Illegal header value b'Bearer sl_...'` would have reached stderr at exit, `on_error` hooks, and the exception `close()` raises. Keys the server issues (`sl_live_<hex>`) are unaffected.
55
+ - `logify360 init` and `logify360 test` no longer echo a malformed key. The same keys as above (whitespace or a control character inside, or any non-ASCII character) used to be sent to `/api/v1/me/key-info`, and the wizard printed httpx's error for them, which quotes the key, inside "Network error contacting ...". The key is now checked where the wizard first receives it, before any request and before `.env` is touched, and refused with `[logify360] --api-key contains whitespace or control characters. Copy the key again from the dashboard.` (naming `LOGIFY360_API_KEY`, or "The pasted key" at the prompt, when it came from there). `fetch_key_info` repeats the check, and its network error now shows the host and the exception class (`ConnectError`, `ReadTimeout`, ...) instead of the exception's own text. Padding around the key is stripped, and a blank `--api-key` falls through to `LOGIFY360_API_KEY`. Normal keys (`sl_live_<hex>`) are unaffected.
56
+ - `logify360 init`: an empty `LOGIFY360_API_KEY=` line in `.env` is no longer read as the value of the next line, and a blank `LOGIFY360_API_BASE` falls back to the hosted API instead of becoming an empty base URL (the same two fixes as `@logify360/cli` 0.3.4).
57
+
58
+ ### Changed
59
+
60
+ - ruff and mypy now target Python 3.10, the package's floor (they still said 3.9). No runtime change.
61
+
62
+ ## 0.8.1
63
+
64
+ ### Changed
65
+
66
+ - Default API host is `https://api.logify360.com`. Affects `DEFAULT_BASE_URL` and the `observe_openai` / `observe_anthropic` / `observe_gemini` default proxy URLs. Set `LOGIFY360_BASE_URL` or pass `base_url=` to override either way.
67
+
68
+ ## 0.8.0
69
+
70
+ FastAPI / ASGI auto-instrumentation - one line traces every HTTP request.
71
+
72
+ ### Added
73
+
74
+ - `Logify360Middleware` (and `install_logify360_middleware(app, ...)`): add it with `app.add_middleware(Logify360Middleware, api_key=...)` (or `client=`) and every request opens a trace + a root span named `"<METHOD> <path>"`. Clean responses end `completed`; a 5xx or unhandled exception ends `error` and the exception is re-raised untouched. The trace is exposed to handlers as `request.state.logify360` (`{trace, span, headers, trace_id, span_id}`) so nested `observe_*` LLM calls link automatically.
75
+ - Sampling and tail-based error capture come from the underlying client, so sampled-out successful requests do zero network I/O while errors are always recorded. Health / metrics / docs routes are skipped by default (`skip_paths=` to override).
76
+ - The middleware is pure ASGI (it does not import FastAPI), so it also works with Starlette, Litestar, Quart, and any ASGI app. New `logify360[fastapi]` optional extra.
77
+
78
+ ### Security
79
+
80
+ - Query strings are **not** captured into trace metadata by default (they routinely carry secrets/PII: OAuth `code`/`state`, reset tokens, signed-URL signatures). Opt in with `capture_query_string=True`.
81
+ - The exception re-raise path uses a `_safe_str` helper so a handler exception whose `__str__` itself raises can never replace the original exception on its way back up.
82
+
83
+ ### Verified
84
+
85
+ - 8 tests (success trace + `request.state` injection, default-path skipping, error capture + re-raise, build-from-`api_key`, missing-credentials guard, query-string off-by-default + opt-in, and broken-`__str__` re-raise). `ruff` clean; `mypy --strict` clean on the new module.
86
+
87
+ ## 0.7.0
88
+
89
+ Onboarding CLI release. `pip install logify360` now also installs a `logify360` console command that connects a Python project to Logify360 in one step, the same wizard the Node `@logify360/cli` ships. No extra dependency: it uses only the standard library plus `httpx` (already a runtime dependency).
90
+
91
+ ### Added
92
+
93
+ - `logify360 init` interactive wizard: detects the package manager (`poetry` / `uv` / `pipenv` / `pip`) and which provider libraries are declared, validates the pasted `sl_live_*` key against `/api/v1/me/key-info`, writes `LOGIFY360_API_KEY` into `.env` (idempotent, comment-preserving), installs the right `logify360[...]` extras, and rewrites client construction to route through the proxy.
94
+ - AST-based code patcher (`logify360.cli.code_patcher`): rewrites `OpenAI(...)` / `AsyncOpenAI(...)` into `create_openai()` / `create_async_openai()`, `Anthropic(...)` / `AsyncAnthropic(...)` into `create_anthropic()` / `create_async_anthropic()`, and `genai.configure(...)` into `configure_gemini()`. It strips `api_key` / `base_url`, preserves every other argument, edits by byte offset so formatting and comments survive, and re-parses each file before writing so a patched file always imports.
95
+ - `logify360 test` for a quick key + connectivity check, and `logify360 --version`.
96
+ - Flags: `--dry-run`, `--yes` (non-interactive), `--api-key`, `--server-url` (self-hosting).
97
+ - `[project.scripts]` entry point so the command is available immediately after install. Output forces UTF-8 so the wizard renders on legacy Windows code pages.
98
+
99
+ ### Fixed
100
+
101
+ - `configure_gemini()` now passes `transport="rest"` to `genai.configure(...)`. The default gRPC transport ignores an `https://` `api_endpoint`, so Gemini calls silently bypassed the proxy and were never logged. REST transport honours the proxy URL and sends the key as `x-goog-api-key`, which the proxy authenticates. Found during the CLI end-to-end smoke; OpenAI and Anthropic were already routing correctly.
102
+
103
+ ### Verified
104
+
105
+ - 48 unit tests across env writing, project detection, install-target construction, the AST patcher (including Unicode-offset safety and validity of every emitted file), the key-info client (mocked with `respx`), and the end-to-end wizard in `--dry-run` / `--yes` modes.
106
+ - Production smoke: the full wizard ran against `www.logify360.com`, validated a live key, and previewed then applied a real OpenAI patch.
107
+
108
+ ### Docs
109
+
110
+ - The `/docs/cli` page now documents the Python CLI alongside the Node wizard, with install, walkthrough, before / after diffs for all three providers, and a flag reference.
111
+
112
+ ## 0.6.0
113
+
114
+ LlamaIndex integration release. Drop `Logify360CallbackHandler` onto any LlamaIndex query engine, agent, or workflow and every `CBEventType` (LLM, RETRIEVE, EMBEDDING, FUNCTION_CALL, QUERY) becomes a typed Logify360 span with parent / child linkage matching the framework's per-event UUIDs.
115
+
116
+ ### Added
117
+
118
+ - `logify360.integrations.llama_index.Logify360CallbackHandler` - subclasses LlamaIndex's `BaseCallbackHandler` so `Settings.callback_manager.add_handler(handler)` is the entire integration. Maps each event type to a Logify360 `span_type`, extracts token usage from the `response.raw.usage` shape OpenAI / LiteLLM / Anthropic LLM backends share, summarises retrieved nodes (count + top scores) instead of dumping full text, and truncates large inputs / outputs at 16 KB by default.
119
+ - Optional dependency: `pip install "logify360[llama-index]"` (pulls in `llama-index-core>=0.10.0`). Also rolled into the `[all]` extra.
120
+ - `llamaindex` / `llama-index` / `langgraph` keywords on the PyPI listing for discoverability.
121
+
122
+ ### Verified
123
+
124
+ - 7 unit tests with `respx` mock the ingest HTTP layer and exercise event dispatch, parent linkage, exception handling, external-trace lifecycle, and retrieved-node summarisation. Run with `pytest tests/test_integrations_llama_index.py`.
125
+ - An integration smoke script (`scripts/test_llama_index_integration.py`) drives a synthetic query through the real `CallbackManager` + `CBEventType` + `EventPayload` from `llama-index-core 0.14.22` and asserts the on-wire shape (trace POST + span POSTs + PATCHes). Setting `LOGIFY360_LIVE_URL` switches it to live mode against a real server.
126
+
127
+ ### Docs
128
+
129
+ - New LlamaIndex docs page at `/docs/integrations/llamaindex` covering install, minimal setup, CBEventType → span_type mapping, trace-tree shape, long-lived trace lifecycle, proxy pairing for cost, and prompt-version linking.
130
+
131
+ ## 0.5.1
132
+
133
+ PyPI metadata fixes + docs polish. No runtime API changes (already-shipped 0.5 features remain identical: `Logify360CallbackHandler` for LangChain / LangGraph, `observe_ollama`, async helpers, sampling).
134
+
135
+ ### Fixed
136
+
137
+ - `Repository` and `Issues` URLs in `pyproject.toml` pointed at a stale GitHub org. PyPI sidebar links were 404. Updated to the correct org.
138
+
139
+ ### Added
140
+
141
+ - New `[langchain]` optional dependency (`pip install "logify360[langchain]"` now pulls in `langchain-core>=0.1.0`). Also rolled into the `[all]` extra. The `Logify360CallbackHandler` already shipped in 0.5.0 but users had to install LangChain manually.
142
+ - `ollama` keyword for PyPI search discoverability.
143
+ - `Changelog` URL so the PyPI sidebar links straight to `CHANGELOG.md`.
144
+
145
+ ### Docs
146
+
147
+ - New Ollama section in the README documenting `observe_ollama()` against a local Ollama via the OpenAI-compatible client.
148
+ - New LangChain / LangGraph section showing the `Logify360CallbackHandler` plug-in pattern.
149
+ - Em dash removal across the README and the package description for a cleaner read on the PyPI page.
150
+
151
+ ## 0.2.0
152
+
153
+ Async-first integration release. Verifies the SDK behaves correctly under
154
+ FastAPI / Django async views / asyncio.gather pipelines.
155
+
156
+ - **New:** `create_async_openai()` returning an `openai.AsyncOpenAI` client
157
+ pre-configured for the Logify360 proxy. Drop-in for FastAPI handlers and
158
+ any `await client.chat.completions.create(...)` flow.
159
+ - **New:** `create_async_anthropic()` returning an `anthropic.AsyncAnthropic`
160
+ client pre-configured for the Logify360 proxy.
161
+ - **New:** test suite verifying `asyncio.gather` of 50 concurrent spans
162
+ preserves trace POST → span POST → span PATCH ordering with no orphaned
163
+ PATCH requests (the failure mode flagged by CLAUDE.md gotcha #10).
164
+ - **New:** end-to-end FastAPI integration test (ASGI transport + respx
165
+ mocking) confirming the ASGI lifecycle is compatible with the SDK's
166
+ background thread pool.
167
+ - No breaking changes - existing sync callers and `observe*()` async support
168
+ are unchanged.
169
+
170
+ ## 0.1.0
171
+
172
+ Initial release of the Logify360 Python SDK.
173
+
174
+ - `Logify360Client`, `TraceHandle`, `SpanHandle` - core tracing primitives
175
+ - Context-manager support so `end()` is called automatically
176
+ - `observe()`, `observe_openai()`, `observe_anthropic()`, `observe_gemini()`
177
+ - boilerplate-free helpers with auto-parsed usage
178
+ - `create_openai()`, `create_anthropic()`, `create_gemini()`,
179
+ `configure_gemini()` - proxy-mode integrations
180
+ - Background ingest with timeout + ordering guarantees so observability
181
+ never blocks user code or loses spans to race conditions
182
+ - Sync **and** async callables supported by every `observe*()` helper