usegradient 1.3.1__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 (31) hide show
  1. usegradient-1.3.1/.gitignore +42 -0
  2. usegradient-1.3.1/PKG-INFO +235 -0
  3. usegradient-1.3.1/README.md +208 -0
  4. usegradient-1.3.1/demo_walkthrough.py +733 -0
  5. usegradient-1.3.1/examples/sdk_first_agent.py +49 -0
  6. usegradient-1.3.1/gradient_sdk/__init__.py +122 -0
  7. usegradient-1.3.1/gradient_sdk/agent.py +368 -0
  8. usegradient-1.3.1/gradient_sdk/client.py +1059 -0
  9. usegradient-1.3.1/gradient_sdk/context.py +91 -0
  10. usegradient-1.3.1/gradient_sdk/credentials.py +92 -0
  11. usegradient-1.3.1/gradient_sdk/decorators.py +202 -0
  12. usegradient-1.3.1/gradient_sdk/errors.py +30 -0
  13. usegradient-1.3.1/gradient_sdk/graph.py +190 -0
  14. usegradient-1.3.1/gradient_sdk/integrations/__init__.py +7 -0
  15. usegradient-1.3.1/gradient_sdk/integrations/agent.py +263 -0
  16. usegradient-1.3.1/gradient_sdk/integrations/anthropic.py +51 -0
  17. usegradient-1.3.1/gradient_sdk/integrations/openai.py +105 -0
  18. usegradient-1.3.1/gradient_sdk/model_catalog.py +75 -0
  19. usegradient-1.3.1/gradient_sdk/models.py +370 -0
  20. usegradient-1.3.1/gradient_sdk/py.typed +1 -0
  21. usegradient-1.3.1/gradient_sdk/runtime.py +1474 -0
  22. usegradient-1.3.1/gradient_sdk/session.py +132 -0
  23. usegradient-1.3.1/gradient_sdk/tools.py +156 -0
  24. usegradient-1.3.1/gradient_sdk/tracing.py +361 -0
  25. usegradient-1.3.1/pyproject.toml +38 -0
  26. usegradient-1.3.1/tests/test_agent.py +156 -0
  27. usegradient-1.3.1/tests/test_client.py +445 -0
  28. usegradient-1.3.1/tests/test_graph.py +74 -0
  29. usegradient-1.3.1/tests/test_integrations.py +84 -0
  30. usegradient-1.3.1/tests/test_runtime.py +407 -0
  31. usegradient-1.3.1/tests/test_tracing.py +133 -0
@@ -0,0 +1,42 @@
1
+ # macOS
2
+ .DS_Store
3
+
4
+ # Editor / agent local state
5
+ .claude/settings.local.json
6
+ .idea/
7
+ .vscode/
8
+
9
+ # Belt-and-suspenders: never commit env files or local CLI credentials
10
+ # (subproject .gitignores already cover these, but keep them here too in
11
+ # case a new top-level service forgets).
12
+ .env
13
+ .env.*
14
+ !.env.example
15
+ credentials
16
+
17
+ # Belt-and-suspenders for build / dependency artifacts in any subproject.
18
+ **/node_modules/
19
+ **/.next/
20
+ **/dist/
21
+ **/build/
22
+ **/*.tsbuildinfo
23
+ **/.vercel/
24
+ **/.turbo/
25
+ **/__pycache__/
26
+ **/*.py[cod]
27
+
28
+ # Go (for the upcoming registry/ and proxy/)
29
+ **/vendor/
30
+ *.exe
31
+ *.test
32
+ *.out
33
+
34
+ registry/registry
35
+ proxy/proxy
36
+ .vercel
37
+ .env*
38
+
39
+ test-langgraph-agent-demo/*
40
+ digisource-hr-agent-demo/*
41
+ output/*
42
+ tmp/*
@@ -0,0 +1,235 @@
1
+ Metadata-Version: 2.4
2
+ Name: usegradient
3
+ Version: 1.3.1
4
+ Summary: Python SDK for the Gradient agent runtime and observability platform
5
+ Project-URL: Homepage, https://usegradient.dev
6
+ Project-URL: Documentation, https://github.com/use-gradient/gradient/tree/main/sdk/python
7
+ Project-URL: Repository, https://github.com/use-gradient/gradient
8
+ Project-URL: Issues, https://github.com/use-gradient/gradient/issues
9
+ Author: Gradient
10
+ License-Expression: MIT
11
+ Keywords: agents,gradient,llm,observability,tracing
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.11
21
+ Provides-Extra: anthropic
22
+ Requires-Dist: anthropic>=0.25.0; extra == 'anthropic'
23
+ Provides-Extra: openai
24
+ Requires-Dist: openai>=1.0.0; extra == 'openai'
25
+ Provides-Extra: test
26
+ Description-Content-Type: text/markdown
27
+
28
+ # Gradient Python SDK
29
+
30
+ Typed, no-dependency Python client for the Gradient console API.
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pip install usegradient
36
+ ```
37
+
38
+ Optional provider extras:
39
+
40
+ ```bash
41
+ pip install "usegradient[anthropic,openai]"
42
+ ```
43
+
44
+ For local development from this repository:
45
+
46
+ ```bash
47
+ pip install -e ./sdk/python
48
+ ```
49
+
50
+ ## Credentials
51
+
52
+ The client uses the same credentials file and environment variables as the Go CLI:
53
+
54
+ - `~/.gradient/credentials`
55
+ - `GRADIENT_API_KEY`
56
+ - `GRADIENT_API_URL`
57
+ - `GRADIENT_REGISTRY_URL`
58
+ - `GRADIENT_PROXY_URL`
59
+
60
+ Environment variables override the credentials file.
61
+
62
+ ## Example
63
+
64
+ ```python
65
+ import os
66
+
67
+ from gradient_sdk import Agent, Environment, Gradient, Model, Trace, tool
68
+
69
+
70
+ @tool
71
+ def lookup_policy(topic: str) -> dict[str, str]:
72
+ """Look up an HR policy by topic."""
73
+
74
+ policies = {
75
+ "parental_leave": {
76
+ "policy_id": "HR-LEAVE-2026",
77
+ "answer": "Employees receive 16 weeks of paid parental leave.",
78
+ },
79
+ "general": {
80
+ "policy_id": "HR-GENERAL-2026",
81
+ "answer": "Contact HR for general policy questions.",
82
+ },
83
+ }
84
+ return policies.get(topic, policies["general"])
85
+
86
+
87
+ HR_AGENT = Agent(
88
+ Model.CLAUDE_SONNET_5,
89
+ tools=[lookup_policy],
90
+ instructions=(
91
+ "You are a friendly HR assistant. When a question needs policy details, "
92
+ "call lookup_policy with topic 'parental_leave' or 'general' before answering."
93
+ ),
94
+ )
95
+
96
+ if __name__ == "__main__":
97
+ gradient = Gradient(
98
+ project=os.getenv("GRADIENT_PROJECT", "my-first-agent"),
99
+ environment=Environment.python(
100
+ python="3.12",
101
+ packages=["usegradient", "anthropic"],
102
+ env={"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]},
103
+ ),
104
+ )
105
+
106
+ handle = gradient.run(
107
+ HR_AGENT,
108
+ "How much paid parental leave do I receive?",
109
+ trace=Trace.full(),
110
+ )
111
+
112
+ print(handle.stdout)
113
+ print(handle.trace_url)
114
+ ```
115
+
116
+ The SDK prints progress for credentials, routing, source packaging, project/environment cache resolution, cold start, source upload, dependency cache, remote run, and cleanup.
117
+
118
+ Use the same object for builds, deployments, and replays:
119
+
120
+ ```python
121
+ build = gradient.build()
122
+ deployment = gradient.deploy(HR_AGENT, name="hr-policy-agent", trace=Trace.runtime(), scale=1)
123
+ report = HR_AGENT.evaluate(
124
+ [{"input": "How much leave?", "expected_output": "HR-LEAVE-2026"}],
125
+ {"mentions_policy": lambda row: row["expected_output"] in row["text"]},
126
+ )
127
+
128
+ print(build.source_hash)
129
+ print(deployment.url)
130
+ print(report["results"][0]["scores"])
131
+ ```
132
+
133
+ For lower-level machine control:
134
+
135
+ ```python
136
+ from gradient_sdk import GradientClient, default_machine_template
137
+
138
+ client = GradientClient.from_credentials()
139
+
140
+ print(client.whoami())
141
+
142
+ template = default_machine_template(
143
+ image="registry.usegradient.dev/my-org/my-project@sha256:...",
144
+ project="my-project",
145
+ region="sjc",
146
+ )
147
+
148
+ machine = client.create_machine(template)
149
+ client.wait_machine_state(machine.id)
150
+ print(machine.id, machine.proxy_url)
151
+
152
+ client.delete_machine(machine.id)
153
+ ```
154
+
155
+ ## Publishing
156
+
157
+ Creating a GitHub release publishes `usegradient` to PyPI via
158
+ [`.github/workflows/publish-python-sdk.yml`](../../.github/workflows/publish-python-sdk.yml).
159
+ The workflow runs on `release: published`, skips prereleases, syncs the package
160
+ version from the release tag (for example `v0.3.0` → `0.3.0`), runs tests,
161
+ builds, and uploads the wheel and sdist using the `PYPI_API_TOKEN` repository
162
+ secret.
163
+
164
+ Configure PyPI publishing once:
165
+
166
+ 1. Create the `usegradient` project on [pypi.org](https://pypi.org/) (first release only).
167
+ 2. Create a PyPI API token with publish access to the `usegradient` project.
168
+ 3. Add it to the GitHub repository as a `PYPI_API_TOKEN` Actions secret.
169
+
170
+ ## API Coverage
171
+
172
+ - `Gradient(project=..., environment=...)`
173
+ - `Agent(Model.GPT_5_6_LUNA, tools=...)` or `Agent(Model.CLAUDE_HAIKU_4_5, tools=...)`
174
+ - `Model.*` exhaustive enum for SDK-supported OpenAI and Anthropic chat/tool models
175
+ - `RunContext(project=..., session_id=None, user_id=None, metadata={})`
176
+ - `@tool`, `@task`, `@agent.step`
177
+ - `agent.run(input)` / `agent.stream(input)` / `agent.deploy(name)` / `agent.evaluate(dataset, evaluators)`
178
+ - `Environment.python(...)` / `Environment.node(...)`
179
+ - `Trace.off()` / `Trace.semantic()` / `Trace.runtime()` / `Trace.full()`
180
+ - `Gradient.build(target=None)`
181
+ - `Gradient.run(target, input, trace=Trace.full(), keep=False)`
182
+ - `Gradient.deploy(target, name=None, trace=Trace.full(), scale=None)`
183
+ - `Gradient.replay(run_id, seed=None, freeze_time=None, egress=None, trace=None)`
184
+ - `Gradient.benchmark(target, input, iterations=...)`
185
+ - `RunHandle.result()` / `cancel()` / `replay()` / `events()` / `logs()` / `spans()` / `to_dataset()`
186
+ - `DeploymentHandle.refresh()` / `start()` / `stop()` / `scale()` / `delete()` / `runs()`
187
+ - `BuildHandle.logs()`
188
+ - `whoami()`
189
+ - `ensure_routable()`
190
+ - `list_projects()` / `ensure_project(name)`
191
+ - `resolve_environment(project=..., environment=..., source=...)`
192
+ - `list_environment_versions(project=None)`
193
+ - `create_machine(template)`
194
+ - `create_traced_machine(template, trace_mode="full", metadata=None, wait=False)`
195
+ - `list_machines()`
196
+ - `delete_machine(machine_id)`
197
+ - `wait_machine_state(machine_id, state="started")`
198
+ - `exec_machine(machine_id, command, stdin=None, timeout_seconds=60, trace=True, watch=None)`
199
+ - `list_secrets()`
200
+ - `update_secrets(values)` / `set_secret(name, value)` / `unset_secret(name)`
201
+ - `create_trace_run(trace_mode="proxy", template=None, metadata=None, capture_policy=None)`
202
+ - `attach_trace_run(run_id, machine_id=..., proxy_url=None)`
203
+ - `finish_trace_run(run_id)`
204
+ - `update_trace_policy(run_id, capture_policy)`
205
+ - `delete_trace_run(run_id)`
206
+ - `get_trace_run(run_id)`
207
+ - `list_trace_runs(limit=50)`
208
+ - `list_trace_events(run_id, limit=1000)`
209
+ - `list_trace_spans(run_id, kind=None, status=None, query=None)`
210
+ - `list_trace_sessions(limit=100, query=None)`
211
+ - `list_session_spans(session_id)`
212
+ - `trace_metrics(days=30)`
213
+ - `query_traces(sql, limit=100)`
214
+ - `create_trace_annotation(name=..., value=..., span_id=...)`
215
+ - `list_trace_annotations(...)`
216
+ - `delete_trace_annotation(annotation_id)`
217
+ - `create_dataset(name, description=None)`
218
+ - `list_datasets()`
219
+ - `get_dataset(dataset_id)`
220
+ - `delete_dataset(dataset_id)`
221
+ - `add_dataset_example(dataset_id, input=..., expected_output=...)`
222
+ - `list_dataset_examples(dataset_id)`
223
+ - `delete_dataset_example(dataset_id, example_id)`
224
+ - `create_experiment(dataset_id=..., name=...)`
225
+ - `list_experiments()`
226
+ - `get_experiment(experiment_id)`
227
+ - `create_experiment_run(experiment_id, name=...)`
228
+ - `record_experiment_results(experiment_id, run_id, results)`
229
+ - `update_experiment_run_status(experiment_id, run_id, status)`
230
+ - `run_experiment(dataset_id, task=..., evaluators=...)`
231
+ - `ingest_trace_event(run_id, ingest_token, event_type=..., data=None)`
232
+ - `ingest_trace_events(run_id, ingest_token, events)`
233
+ - `trace_template(template, trace)`
234
+ - `proxy_url(machine_id, slug=None, proxy_base=None)`
235
+ - `default_machine_template(image, project=None, ...)`
@@ -0,0 +1,208 @@
1
+ # Gradient Python SDK
2
+
3
+ Typed, no-dependency Python client for the Gradient console API.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install usegradient
9
+ ```
10
+
11
+ Optional provider extras:
12
+
13
+ ```bash
14
+ pip install "usegradient[anthropic,openai]"
15
+ ```
16
+
17
+ For local development from this repository:
18
+
19
+ ```bash
20
+ pip install -e ./sdk/python
21
+ ```
22
+
23
+ ## Credentials
24
+
25
+ The client uses the same credentials file and environment variables as the Go CLI:
26
+
27
+ - `~/.gradient/credentials`
28
+ - `GRADIENT_API_KEY`
29
+ - `GRADIENT_API_URL`
30
+ - `GRADIENT_REGISTRY_URL`
31
+ - `GRADIENT_PROXY_URL`
32
+
33
+ Environment variables override the credentials file.
34
+
35
+ ## Example
36
+
37
+ ```python
38
+ import os
39
+
40
+ from gradient_sdk import Agent, Environment, Gradient, Model, Trace, tool
41
+
42
+
43
+ @tool
44
+ def lookup_policy(topic: str) -> dict[str, str]:
45
+ """Look up an HR policy by topic."""
46
+
47
+ policies = {
48
+ "parental_leave": {
49
+ "policy_id": "HR-LEAVE-2026",
50
+ "answer": "Employees receive 16 weeks of paid parental leave.",
51
+ },
52
+ "general": {
53
+ "policy_id": "HR-GENERAL-2026",
54
+ "answer": "Contact HR for general policy questions.",
55
+ },
56
+ }
57
+ return policies.get(topic, policies["general"])
58
+
59
+
60
+ HR_AGENT = Agent(
61
+ Model.CLAUDE_SONNET_5,
62
+ tools=[lookup_policy],
63
+ instructions=(
64
+ "You are a friendly HR assistant. When a question needs policy details, "
65
+ "call lookup_policy with topic 'parental_leave' or 'general' before answering."
66
+ ),
67
+ )
68
+
69
+ if __name__ == "__main__":
70
+ gradient = Gradient(
71
+ project=os.getenv("GRADIENT_PROJECT", "my-first-agent"),
72
+ environment=Environment.python(
73
+ python="3.12",
74
+ packages=["usegradient", "anthropic"],
75
+ env={"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"]},
76
+ ),
77
+ )
78
+
79
+ handle = gradient.run(
80
+ HR_AGENT,
81
+ "How much paid parental leave do I receive?",
82
+ trace=Trace.full(),
83
+ )
84
+
85
+ print(handle.stdout)
86
+ print(handle.trace_url)
87
+ ```
88
+
89
+ The SDK prints progress for credentials, routing, source packaging, project/environment cache resolution, cold start, source upload, dependency cache, remote run, and cleanup.
90
+
91
+ Use the same object for builds, deployments, and replays:
92
+
93
+ ```python
94
+ build = gradient.build()
95
+ deployment = gradient.deploy(HR_AGENT, name="hr-policy-agent", trace=Trace.runtime(), scale=1)
96
+ report = HR_AGENT.evaluate(
97
+ [{"input": "How much leave?", "expected_output": "HR-LEAVE-2026"}],
98
+ {"mentions_policy": lambda row: row["expected_output"] in row["text"]},
99
+ )
100
+
101
+ print(build.source_hash)
102
+ print(deployment.url)
103
+ print(report["results"][0]["scores"])
104
+ ```
105
+
106
+ For lower-level machine control:
107
+
108
+ ```python
109
+ from gradient_sdk import GradientClient, default_machine_template
110
+
111
+ client = GradientClient.from_credentials()
112
+
113
+ print(client.whoami())
114
+
115
+ template = default_machine_template(
116
+ image="registry.usegradient.dev/my-org/my-project@sha256:...",
117
+ project="my-project",
118
+ region="sjc",
119
+ )
120
+
121
+ machine = client.create_machine(template)
122
+ client.wait_machine_state(machine.id)
123
+ print(machine.id, machine.proxy_url)
124
+
125
+ client.delete_machine(machine.id)
126
+ ```
127
+
128
+ ## Publishing
129
+
130
+ Creating a GitHub release publishes `usegradient` to PyPI via
131
+ [`.github/workflows/publish-python-sdk.yml`](../../.github/workflows/publish-python-sdk.yml).
132
+ The workflow runs on `release: published`, skips prereleases, syncs the package
133
+ version from the release tag (for example `v0.3.0` → `0.3.0`), runs tests,
134
+ builds, and uploads the wheel and sdist using the `PYPI_API_TOKEN` repository
135
+ secret.
136
+
137
+ Configure PyPI publishing once:
138
+
139
+ 1. Create the `usegradient` project on [pypi.org](https://pypi.org/) (first release only).
140
+ 2. Create a PyPI API token with publish access to the `usegradient` project.
141
+ 3. Add it to the GitHub repository as a `PYPI_API_TOKEN` Actions secret.
142
+
143
+ ## API Coverage
144
+
145
+ - `Gradient(project=..., environment=...)`
146
+ - `Agent(Model.GPT_5_6_LUNA, tools=...)` or `Agent(Model.CLAUDE_HAIKU_4_5, tools=...)`
147
+ - `Model.*` exhaustive enum for SDK-supported OpenAI and Anthropic chat/tool models
148
+ - `RunContext(project=..., session_id=None, user_id=None, metadata={})`
149
+ - `@tool`, `@task`, `@agent.step`
150
+ - `agent.run(input)` / `agent.stream(input)` / `agent.deploy(name)` / `agent.evaluate(dataset, evaluators)`
151
+ - `Environment.python(...)` / `Environment.node(...)`
152
+ - `Trace.off()` / `Trace.semantic()` / `Trace.runtime()` / `Trace.full()`
153
+ - `Gradient.build(target=None)`
154
+ - `Gradient.run(target, input, trace=Trace.full(), keep=False)`
155
+ - `Gradient.deploy(target, name=None, trace=Trace.full(), scale=None)`
156
+ - `Gradient.replay(run_id, seed=None, freeze_time=None, egress=None, trace=None)`
157
+ - `Gradient.benchmark(target, input, iterations=...)`
158
+ - `RunHandle.result()` / `cancel()` / `replay()` / `events()` / `logs()` / `spans()` / `to_dataset()`
159
+ - `DeploymentHandle.refresh()` / `start()` / `stop()` / `scale()` / `delete()` / `runs()`
160
+ - `BuildHandle.logs()`
161
+ - `whoami()`
162
+ - `ensure_routable()`
163
+ - `list_projects()` / `ensure_project(name)`
164
+ - `resolve_environment(project=..., environment=..., source=...)`
165
+ - `list_environment_versions(project=None)`
166
+ - `create_machine(template)`
167
+ - `create_traced_machine(template, trace_mode="full", metadata=None, wait=False)`
168
+ - `list_machines()`
169
+ - `delete_machine(machine_id)`
170
+ - `wait_machine_state(machine_id, state="started")`
171
+ - `exec_machine(machine_id, command, stdin=None, timeout_seconds=60, trace=True, watch=None)`
172
+ - `list_secrets()`
173
+ - `update_secrets(values)` / `set_secret(name, value)` / `unset_secret(name)`
174
+ - `create_trace_run(trace_mode="proxy", template=None, metadata=None, capture_policy=None)`
175
+ - `attach_trace_run(run_id, machine_id=..., proxy_url=None)`
176
+ - `finish_trace_run(run_id)`
177
+ - `update_trace_policy(run_id, capture_policy)`
178
+ - `delete_trace_run(run_id)`
179
+ - `get_trace_run(run_id)`
180
+ - `list_trace_runs(limit=50)`
181
+ - `list_trace_events(run_id, limit=1000)`
182
+ - `list_trace_spans(run_id, kind=None, status=None, query=None)`
183
+ - `list_trace_sessions(limit=100, query=None)`
184
+ - `list_session_spans(session_id)`
185
+ - `trace_metrics(days=30)`
186
+ - `query_traces(sql, limit=100)`
187
+ - `create_trace_annotation(name=..., value=..., span_id=...)`
188
+ - `list_trace_annotations(...)`
189
+ - `delete_trace_annotation(annotation_id)`
190
+ - `create_dataset(name, description=None)`
191
+ - `list_datasets()`
192
+ - `get_dataset(dataset_id)`
193
+ - `delete_dataset(dataset_id)`
194
+ - `add_dataset_example(dataset_id, input=..., expected_output=...)`
195
+ - `list_dataset_examples(dataset_id)`
196
+ - `delete_dataset_example(dataset_id, example_id)`
197
+ - `create_experiment(dataset_id=..., name=...)`
198
+ - `list_experiments()`
199
+ - `get_experiment(experiment_id)`
200
+ - `create_experiment_run(experiment_id, name=...)`
201
+ - `record_experiment_results(experiment_id, run_id, results)`
202
+ - `update_experiment_run_status(experiment_id, run_id, status)`
203
+ - `run_experiment(dataset_id, task=..., evaluators=...)`
204
+ - `ingest_trace_event(run_id, ingest_token, event_type=..., data=None)`
205
+ - `ingest_trace_events(run_id, ingest_token, events)`
206
+ - `trace_template(template, trace)`
207
+ - `proxy_url(machine_id, slug=None, proxy_base=None)`
208
+ - `default_machine_template(image, project=None, ...)`