uncoil-sdk 0.6.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.
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.4
2
+ Name: uncoil-sdk
3
+ Version: 0.6.0
4
+ Summary: Observability, runtime enforcement, and tracing for AI agents.
5
+ Author-email: Uncoil Team <hello@uncoil.dev>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://www.uncoil.dev
8
+ Project-URL: Documentation, https://www.uncoil.dev/docs
9
+ Project-URL: Bug Tracker, https://github.com/Uncoil-dev/Uncoil/issues
10
+ Project-URL: Source, https://github.com/Uncoil-dev/Uncoil
11
+ Keywords: ai,agents,observability,tracing,llm,llm-observability,agent-tracing,opentelemetry,cost-tracking,langchain,openai,anthropic
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Operating System :: OS Independent
14
+ Requires-Python: >=3.7
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: requests>=2.25.0
17
+ Requires-Dist: httpx>=0.24.0
18
+ Provides-Extra: openai
19
+ Requires-Dist: openai>=1.0; extra == "openai"
20
+ Provides-Extra: anthropic
21
+ Requires-Dist: anthropic>=0.25; extra == "anthropic"
22
+ Provides-Extra: langgraph
23
+ Requires-Dist: langchain-core>=0.2; extra == "langgraph"
24
+ Requires-Dist: langgraph>=0.2; extra == "langgraph"
25
+ Provides-Extra: test
26
+ Requires-Dist: pytest>=8; extra == "test"
27
+ Requires-Dist: pytest-asyncio>=0.24; extra == "test"
28
+ Requires-Dist: httpx2==2.13.1; extra == "test"
29
+ Requires-Dist: openai==3.31.0; extra == "test"
30
+ Requires-Dist: anthropic==1.14.0; extra == "test"
31
+ Requires-Dist: langgraph==1.2.15; extra == "test"
32
+ Requires-Dist: langchain-core==1.6.10; extra == "test"
33
+
34
+ # Uncoil Python SDK
35
+
36
+ Observability and tracing for AI agents. Gain full visibility into your agent's decision-making process, tool usage, and LLM costs.
37
+
38
+ Uncoil records each agent run as one trace: every model call, tool call, retry, failure and cost. It also ingests OpenTelemetry. Free plan available. Docs: [uncoil.dev/docs](https://www.uncoil.dev/docs). New to agent observability? Read the [complete guide](https://www.uncoil.dev/blog/ai-agent-observability-complete-guide).
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ pip install uncoil-sdk
44
+ ```
45
+
46
+ ## Quickstart
47
+
48
+ 1. Get your API Key from the [Uncoil Dashboard](https://www.uncoil.dev).
49
+ 2. Set it as an environment variable:
50
+
51
+ ```bash
52
+ export UNCOIL_API_KEY=your_sk_live_...
53
+ ```
54
+
55
+ 3. Instrument your agent:
56
+
57
+ ```python
58
+ from uncoil import trace_agent, llm_call, tool_call
59
+
60
+ @trace_agent(environment="development", release="quickstart")
61
+ async def my_agent(query: str):
62
+ # LLM Call
63
+ llm_call(
64
+ input_data=query,
65
+ output_data="Thinking...",
66
+ model_id="gpt-4o",
67
+ cost_usd=0.002,
68
+ latency_ms=800
69
+ )
70
+
71
+ # Tool Use
72
+ tool_call(
73
+ tool_name="web_search",
74
+ input_data={"q": query},
75
+ output_data="Found results...",
76
+ latency_ms=1200
77
+ )
78
+
79
+ return "Final Answer"
80
+ ```
81
+
82
+ ## Steps
83
+
84
+ Inside a `@trace_agent` function, every step you record is linked to the step it ran in, so the trace graph
85
+ shows which step called which and how loops repeat. Use `span` for a block and `observe` for a function:
86
+
87
+ ```python
88
+ import uncoil
89
+
90
+ @uncoil.observe(type="tool")
91
+ def search_docs(query):
92
+ return search(query)
93
+
94
+ @uncoil.trace_agent()
95
+ def answer(question):
96
+ for turn in range(5):
97
+ with uncoil.span("think", type="agent"):
98
+ with uncoil.span("chat", type="llm", input=question) as step:
99
+ reply = call_model(question)
100
+ step.set_output(reply.text)
101
+ step.set(model="claude-sonnet-5-5", input_tokens=reply.usage.input, output_tokens=reply.usage.output)
102
+ if reply.done:
103
+ return reply.text
104
+ search_docs(reply.query)
105
+ ```
106
+
107
+ `type` is one of `agent`, `llm`, `tool`, `retrieval` or `span`. Errors are recorded with their type and
108
+ message. Concurrent runs (threads or asyncio tasks) stay in separate traces. Inputs and outputs have obvious
109
+ secrets (bearer tokens, API keys, JWTs, `password=` values) removed and are capped at 10,000 characters;
110
+ avoid sending anything else you would not want stored.
111
+
112
+ ## Features
113
+
114
+ - **@trace_agent Decorator:** Automatically captures run lifecycle and errors.
115
+ - **Granular Spans:** Manually record LLMs, tools, and custom decisions.
116
+ - **Cost Tracking:** Monitor your infrastructure spend in real-time.
117
+ - **Asynchronous & Synchronous Support:** Works with `async/await` or standard functions.
118
+ - **Session context:** Attach `session_id`, `end_user_id`, `environment`, `release`, and `tags` to connect related traces.
119
+
120
+ ## Feedback and evaluation scores
121
+
122
+ Pass `project_id` when constructing the client to attach end-user feedback and typed scores to traces:
123
+
124
+ ```python
125
+ client = UncoilClient(project_id="your-project-id")
126
+ client.feedback(run_id, True, kind="thumb", comment="Helpful answer")
127
+ client.score(run_id, "answer_quality", 0.92, data_type="numeric")
128
+ ```
129
+
130
+ Feedback and scores use the same API key as ingestion and do not interrupt the agent when delivery fails.
131
+
132
+ ## Prompt deployment
133
+
134
+ Resolve a deployed prompt without shipping a new application build:
135
+
136
+ ```python
137
+ deployed = client.get_prompt("support-agent", "production")
138
+ client.ingest(spanType="llm", input=question, output=answer, promptVersionId=deployed["version"]["id"])
139
+ ```
140
+
141
+ Prompt responses are cached for 60 seconds by default. Use `cache_ttl_seconds=0` or provide a `fallback` for development and guaranteed-availability behavior.
142
+
143
+ ## License
144
+
145
+ MIT
146
+
147
+ ## Provider integrations (0.4.0)
148
+
149
+ Install the optional provider/framework extras you use: `pip install 'uncoil-sdk[openai,anthropic,langgraph]'`.
150
+ Set `UNCOIL_API_KEY` and the existing regional configuration.
151
+
152
+ ```python
153
+ import uncoil
154
+ from openai import OpenAI, AsyncOpenAI
155
+ from anthropic import Anthropic
156
+
157
+ openai = uncoil.wrap_openai(OpenAI())
158
+ anthropic = uncoil.wrap_anthropic(Anthropic())
159
+ async_openai = uncoil.wrap_openai(AsyncOpenAI())
160
+ # Existing create calls keep their provider response types and exceptions.
161
+ reply = openai.responses.create(model=MODEL, input="Hello")
162
+ with anthropic.messages.stream(model=MODEL, max_tokens=100,
163
+ messages=[{"role": "user", "content": "Hello"}]) as stream:
164
+ for text in stream.text_stream:
165
+ print(text, end="")
166
+
167
+ # Add a callback to a compiled LangGraph invocation.
168
+ graph.invoke(inputs, config={"callbacks": [uncoil.langgraph_callback()]})
169
+ ```
170
+
171
+ Wrap sync or async clients once. Supported: OpenAI chat completions and Responses `create` APIs,
172
+ including streams; Anthropic messages `create`/`stream` and beta equivalents, including sync/async
173
+ context managers, text streams and final messages. Close streams when stopping early. Raw response
174
+ resources and unsupported convenience methods are passed through without instrumentation.
175
+
176
+ Calls inside `trace_agent`/`span` inherit their run/parent and custom client. Standalone calls receive
177
+ an independent completed root. LangGraph callbacks preserve graph/node/tool/model parent IDs and
178
+ concurrent runs. Avoid using both framework model callbacks and a provider wrapper for the same call.
179
+ Provider packages remain optional; newer framework extras may require a newer Python than the core SDK.
180
+
181
+ Options: `client=custom_client`, `name="my-agent"`, `capture_input=False`, `capture_output=False`.
182
+ Existing redaction/size caps apply. Records include model, tokens, latency, TTFT, failures and raw usage
183
+ metadata. Costs default to zero with `costKnown=False`; supply
184
+ `calculate_cost=lambda usage: ...` with your own current provider rate card. The callback receives
185
+ `provider`, `model`, `input_tokens`, `output_tokens`, and raw `usage` (including cache details).
186
+ Missing usage is not estimated. Telemetry failures do not change provider results or exceptions.
187
+
188
+ Anthropic total input tokens include uncached input plus cache reads and cache writes; the original
189
+ usage breakdown remains in metadata for rate-card calculations. See the [provider’s token breakdown](https://platform.claude.com/docs/en/build-with-claude/prompt-caching).
190
+
191
+ ## Framework one-liners
192
+
193
+ Quickest start: call `uncoil.init()` once at startup. Every OpenAI, Anthropic and LangChain / LangGraph call is traced, sync and async, with no client wrapping (`UNCOIL_API_KEY` is read from the environment). Libraries that are not installed are skipped, and `uncoil.uninit()` switches it off.
194
+
195
+ ```python
196
+ import uncoil
197
+ uncoil.init()
198
+ ```
199
+
200
+ Calls made through a client you also wrapped with `wrap_openai` are recorded once, and model calls inside LangChain are recorded by the LangChain handler only. The Vercel AI SDK is JavaScript-only.
201
+
202
+ ```python
203
+ import uncoil
204
+
205
+ tools = uncoil.trace_tools({"search_docs": search_docs, "send_email": send_email}) # plain or async functions
206
+ client = uncoil.wrap_openai(OpenAI()) # also wrap_anthropic, langgraph_callback
207
+ ```
208
+
209
+ Copy-paste starters live in [`templates/`](./templates).
210
+
211
+ ## Sampling, redaction and clearer errors
212
+
213
+ ```python
214
+ client = uncoil.UncoilClient(
215
+ sample_rate=0.1, # record about 1 in 10 runs; each run is kept or dropped whole
216
+ redact={"patterns": [re.compile(r"ACME-\d{6}")]}, # emails, phones and card numbers are masked by default; add your own
217
+ on_error=lambda error: log(str(error)), # problems come with a plain-language fix (error.hint)
218
+ )
219
+ ```
220
+
221
+ Spans that report an error are kept even when their run was not sampled (`keep_errors=False` turns that off). Secrets such as bearer tokens and API keys are always removed, and emails, phone numbers and card numbers are masked before anything leaves your app. To send them as they are, pass `redact={"pii": False}`.
222
+
223
+ ## Setup checker
224
+
225
+ Not seeing traces? One command checks your key, region and network, then sends a test trace:
226
+
227
+ ```bash
228
+ uncoil-doctor # or: python -m uncoil doctor
229
+ ```
230
+
231
+ It reads `UNCOIL_API_KEY` (and `UNCOIL_REGION` / `UNCOIL_HOST` if set), tells you in plain words what is wrong and how to fix it, and exits with code 1 if anything fails. Options: `--key`, `--region eu|us`, `--host`, `--no-send` (skip the test trace) and `--json`. The test trace is named "Uncoil setup check" and tagged `uncoil-doctor`; it counts as one trace on your plan.
@@ -0,0 +1,198 @@
1
+ # Uncoil Python SDK
2
+
3
+ Observability and tracing for AI agents. Gain full visibility into your agent's decision-making process, tool usage, and LLM costs.
4
+
5
+ Uncoil records each agent run as one trace: every model call, tool call, retry, failure and cost. It also ingests OpenTelemetry. Free plan available. Docs: [uncoil.dev/docs](https://www.uncoil.dev/docs). New to agent observability? Read the [complete guide](https://www.uncoil.dev/blog/ai-agent-observability-complete-guide).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install uncoil-sdk
11
+ ```
12
+
13
+ ## Quickstart
14
+
15
+ 1. Get your API Key from the [Uncoil Dashboard](https://www.uncoil.dev).
16
+ 2. Set it as an environment variable:
17
+
18
+ ```bash
19
+ export UNCOIL_API_KEY=your_sk_live_...
20
+ ```
21
+
22
+ 3. Instrument your agent:
23
+
24
+ ```python
25
+ from uncoil import trace_agent, llm_call, tool_call
26
+
27
+ @trace_agent(environment="development", release="quickstart")
28
+ async def my_agent(query: str):
29
+ # LLM Call
30
+ llm_call(
31
+ input_data=query,
32
+ output_data="Thinking...",
33
+ model_id="gpt-4o",
34
+ cost_usd=0.002,
35
+ latency_ms=800
36
+ )
37
+
38
+ # Tool Use
39
+ tool_call(
40
+ tool_name="web_search",
41
+ input_data={"q": query},
42
+ output_data="Found results...",
43
+ latency_ms=1200
44
+ )
45
+
46
+ return "Final Answer"
47
+ ```
48
+
49
+ ## Steps
50
+
51
+ Inside a `@trace_agent` function, every step you record is linked to the step it ran in, so the trace graph
52
+ shows which step called which and how loops repeat. Use `span` for a block and `observe` for a function:
53
+
54
+ ```python
55
+ import uncoil
56
+
57
+ @uncoil.observe(type="tool")
58
+ def search_docs(query):
59
+ return search(query)
60
+
61
+ @uncoil.trace_agent()
62
+ def answer(question):
63
+ for turn in range(5):
64
+ with uncoil.span("think", type="agent"):
65
+ with uncoil.span("chat", type="llm", input=question) as step:
66
+ reply = call_model(question)
67
+ step.set_output(reply.text)
68
+ step.set(model="claude-sonnet-5-5", input_tokens=reply.usage.input, output_tokens=reply.usage.output)
69
+ if reply.done:
70
+ return reply.text
71
+ search_docs(reply.query)
72
+ ```
73
+
74
+ `type` is one of `agent`, `llm`, `tool`, `retrieval` or `span`. Errors are recorded with their type and
75
+ message. Concurrent runs (threads or asyncio tasks) stay in separate traces. Inputs and outputs have obvious
76
+ secrets (bearer tokens, API keys, JWTs, `password=` values) removed and are capped at 10,000 characters;
77
+ avoid sending anything else you would not want stored.
78
+
79
+ ## Features
80
+
81
+ - **@trace_agent Decorator:** Automatically captures run lifecycle and errors.
82
+ - **Granular Spans:** Manually record LLMs, tools, and custom decisions.
83
+ - **Cost Tracking:** Monitor your infrastructure spend in real-time.
84
+ - **Asynchronous & Synchronous Support:** Works with `async/await` or standard functions.
85
+ - **Session context:** Attach `session_id`, `end_user_id`, `environment`, `release`, and `tags` to connect related traces.
86
+
87
+ ## Feedback and evaluation scores
88
+
89
+ Pass `project_id` when constructing the client to attach end-user feedback and typed scores to traces:
90
+
91
+ ```python
92
+ client = UncoilClient(project_id="your-project-id")
93
+ client.feedback(run_id, True, kind="thumb", comment="Helpful answer")
94
+ client.score(run_id, "answer_quality", 0.92, data_type="numeric")
95
+ ```
96
+
97
+ Feedback and scores use the same API key as ingestion and do not interrupt the agent when delivery fails.
98
+
99
+ ## Prompt deployment
100
+
101
+ Resolve a deployed prompt without shipping a new application build:
102
+
103
+ ```python
104
+ deployed = client.get_prompt("support-agent", "production")
105
+ client.ingest(spanType="llm", input=question, output=answer, promptVersionId=deployed["version"]["id"])
106
+ ```
107
+
108
+ Prompt responses are cached for 60 seconds by default. Use `cache_ttl_seconds=0` or provide a `fallback` for development and guaranteed-availability behavior.
109
+
110
+ ## License
111
+
112
+ MIT
113
+
114
+ ## Provider integrations (0.4.0)
115
+
116
+ Install the optional provider/framework extras you use: `pip install 'uncoil-sdk[openai,anthropic,langgraph]'`.
117
+ Set `UNCOIL_API_KEY` and the existing regional configuration.
118
+
119
+ ```python
120
+ import uncoil
121
+ from openai import OpenAI, AsyncOpenAI
122
+ from anthropic import Anthropic
123
+
124
+ openai = uncoil.wrap_openai(OpenAI())
125
+ anthropic = uncoil.wrap_anthropic(Anthropic())
126
+ async_openai = uncoil.wrap_openai(AsyncOpenAI())
127
+ # Existing create calls keep their provider response types and exceptions.
128
+ reply = openai.responses.create(model=MODEL, input="Hello")
129
+ with anthropic.messages.stream(model=MODEL, max_tokens=100,
130
+ messages=[{"role": "user", "content": "Hello"}]) as stream:
131
+ for text in stream.text_stream:
132
+ print(text, end="")
133
+
134
+ # Add a callback to a compiled LangGraph invocation.
135
+ graph.invoke(inputs, config={"callbacks": [uncoil.langgraph_callback()]})
136
+ ```
137
+
138
+ Wrap sync or async clients once. Supported: OpenAI chat completions and Responses `create` APIs,
139
+ including streams; Anthropic messages `create`/`stream` and beta equivalents, including sync/async
140
+ context managers, text streams and final messages. Close streams when stopping early. Raw response
141
+ resources and unsupported convenience methods are passed through without instrumentation.
142
+
143
+ Calls inside `trace_agent`/`span` inherit their run/parent and custom client. Standalone calls receive
144
+ an independent completed root. LangGraph callbacks preserve graph/node/tool/model parent IDs and
145
+ concurrent runs. Avoid using both framework model callbacks and a provider wrapper for the same call.
146
+ Provider packages remain optional; newer framework extras may require a newer Python than the core SDK.
147
+
148
+ Options: `client=custom_client`, `name="my-agent"`, `capture_input=False`, `capture_output=False`.
149
+ Existing redaction/size caps apply. Records include model, tokens, latency, TTFT, failures and raw usage
150
+ metadata. Costs default to zero with `costKnown=False`; supply
151
+ `calculate_cost=lambda usage: ...` with your own current provider rate card. The callback receives
152
+ `provider`, `model`, `input_tokens`, `output_tokens`, and raw `usage` (including cache details).
153
+ Missing usage is not estimated. Telemetry failures do not change provider results or exceptions.
154
+
155
+ Anthropic total input tokens include uncached input plus cache reads and cache writes; the original
156
+ usage breakdown remains in metadata for rate-card calculations. See the [provider’s token breakdown](https://platform.claude.com/docs/en/build-with-claude/prompt-caching).
157
+
158
+ ## Framework one-liners
159
+
160
+ Quickest start: call `uncoil.init()` once at startup. Every OpenAI, Anthropic and LangChain / LangGraph call is traced, sync and async, with no client wrapping (`UNCOIL_API_KEY` is read from the environment). Libraries that are not installed are skipped, and `uncoil.uninit()` switches it off.
161
+
162
+ ```python
163
+ import uncoil
164
+ uncoil.init()
165
+ ```
166
+
167
+ Calls made through a client you also wrapped with `wrap_openai` are recorded once, and model calls inside LangChain are recorded by the LangChain handler only. The Vercel AI SDK is JavaScript-only.
168
+
169
+ ```python
170
+ import uncoil
171
+
172
+ tools = uncoil.trace_tools({"search_docs": search_docs, "send_email": send_email}) # plain or async functions
173
+ client = uncoil.wrap_openai(OpenAI()) # also wrap_anthropic, langgraph_callback
174
+ ```
175
+
176
+ Copy-paste starters live in [`templates/`](./templates).
177
+
178
+ ## Sampling, redaction and clearer errors
179
+
180
+ ```python
181
+ client = uncoil.UncoilClient(
182
+ sample_rate=0.1, # record about 1 in 10 runs; each run is kept or dropped whole
183
+ redact={"patterns": [re.compile(r"ACME-\d{6}")]}, # emails, phones and card numbers are masked by default; add your own
184
+ on_error=lambda error: log(str(error)), # problems come with a plain-language fix (error.hint)
185
+ )
186
+ ```
187
+
188
+ Spans that report an error are kept even when their run was not sampled (`keep_errors=False` turns that off). Secrets such as bearer tokens and API keys are always removed, and emails, phone numbers and card numbers are masked before anything leaves your app. To send them as they are, pass `redact={"pii": False}`.
189
+
190
+ ## Setup checker
191
+
192
+ Not seeing traces? One command checks your key, region and network, then sends a test trace:
193
+
194
+ ```bash
195
+ uncoil-doctor # or: python -m uncoil doctor
196
+ ```
197
+
198
+ It reads `UNCOIL_API_KEY` (and `UNCOIL_REGION` / `UNCOIL_HOST` if set), tells you in plain words what is wrong and how to fix it, and exits with code 1 if anything fails. Options: `--key`, `--region eu|us`, `--host`, `--no-send` (skip the test trace) and `--json`. The test trace is named "Uncoil setup check" and tagged `uncoil-doctor`; it counts as one trace on your plan.
@@ -0,0 +1,45 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "uncoil-sdk"
7
+ version = "0.6.0"
8
+ authors = [
9
+ { name="Uncoil Team", email="hello@uncoil.dev" },
10
+ ]
11
+ description = "Observability, runtime enforcement, and tracing for AI agents."
12
+ license = "MIT"
13
+ keywords = ["ai", "agents", "observability", "tracing", "llm", "llm-observability", "agent-tracing", "opentelemetry", "cost-tracking", "langchain", "openai", "anthropic"]
14
+ readme = "README.md"
15
+ requires-python = ">=3.7"
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "Operating System :: OS Independent",
19
+ ]
20
+ dependencies = [
21
+ "requests>=2.25.0",
22
+ "httpx>=0.24.0",
23
+ ]
24
+
25
+ [project.scripts]
26
+ uncoil-doctor = "uncoil.doctor:main"
27
+
28
+ [tool.setuptools.packages.find]
29
+ where = ["src"]
30
+
31
+ [project.urls]
32
+ "Homepage" = "https://www.uncoil.dev"
33
+ "Documentation" = "https://www.uncoil.dev/docs"
34
+ "Bug Tracker" = "https://github.com/Uncoil-dev/Uncoil/issues"
35
+ "Source" = "https://github.com/Uncoil-dev/Uncoil"
36
+
37
+ [tool.pytest.ini_options]
38
+ testpaths = ["tests"]
39
+ pythonpath = ["src"]
40
+
41
+ [project.optional-dependencies]
42
+ openai = ["openai>=1.0"]
43
+ anthropic = ["anthropic>=0.25"]
44
+ langgraph = ["langchain-core>=0.2", "langgraph>=0.2"]
45
+ test = ["pytest>=8", "pytest-asyncio>=0.24", "httpx2==2.13.1", "openai==3.31.0", "anthropic==1.14.0", "langgraph==1.2.15", "langchain-core==1.6.10"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+