databricks-mason 0.1.1.dev0__tar.gz → 0.1.2.dev0__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.
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/PKG-INFO +53 -31
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/README.md +43 -30
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/pyproject.toml +16 -4
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/agent_project.py +13 -2
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/cli.py +5 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/client.py +40 -7
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/deploy.py +65 -16
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/dev.py +44 -10
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/errors.py +21 -0
- databricks_mason-0.1.2.dev0/src/databricks_mason/help.py +139 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/init.py +22 -42
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/langgraph/__init__.py +2 -3
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/memory.py +92 -23
- databricks_mason-0.1.2.dev0/src/databricks_mason/openai/__init__.py +89 -0
- databricks_mason-0.1.2.dev0/src/databricks_mason/openai/mcp.py +94 -0
- databricks_mason-0.1.2.dev0/src/databricks_mason/openai/memory.py +63 -0
- databricks_mason-0.1.2.dev0/src/databricks_mason/openai/sessions.py +159 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/render.py +13 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/runtime/__init__.py +2 -2
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/sessions.py +52 -7
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/store_access.py +50 -21
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/timefmt.py +4 -4
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/tools.py +44 -8
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/tracing.py +1 -1
- databricks_mason-0.1.1.dev0/src/databricks_mason/langgraph/long_running.py +0 -59
- databricks_mason-0.1.1.dev0/src/databricks_mason/langgraph/recovery.py +0 -247
- databricks_mason-0.1.1.dev0/src/databricks_mason/runtime/durability.py +0 -377
- databricks_mason-0.1.1.dev0/src/databricks_mason/templates/mcp_runtime_langgraph.py +0 -96
- databricks_mason-0.1.1.dev0/src/databricks_mason/templates/tool_manifest_runtime.py +0 -143
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/.gitignore +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/NOTICE +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/__init__.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/auth.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/langgraph/mcp.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/langgraph/memory.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/langgraph/session_store.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/mcp.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/memory_store_access.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/models.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/project_config.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/py.typed +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/runtime/background.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/runtime/session_store_client.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/runtime/tool_manifest.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/runtime/tracing.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/runtime/workspace.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/sandbox.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/session_store_access.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/templates/python_tool_langgraph.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/templates/python_tool_test.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/templates/sandbox_mcp.py +0 -0
- {databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/templates/sandbox_mcp_langgraph.py +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: databricks-mason
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2.dev0
|
|
4
4
|
Summary: Databricks integration for Mason
|
|
5
5
|
Author-email: Databricks <agent-feedback@databricks.com>
|
|
6
6
|
License-File: NOTICE
|
|
7
7
|
Requires-Python: >=3.10
|
|
8
8
|
Requires-Dist: click>=8.1
|
|
9
9
|
Requires-Dist: databricks-sdk>=0.49
|
|
10
|
+
Requires-Dist: psycopg[binary]>=3.1
|
|
10
11
|
Requires-Dist: pyyaml>=6.0
|
|
11
12
|
Requires-Dist: rich>=13.7
|
|
12
13
|
Requires-Dist: tomli>=2.0
|
|
@@ -21,6 +22,14 @@ Requires-Dist: langgraph>=1.1.0; extra == 'runtime'
|
|
|
21
22
|
Requires-Dist: mlflow>=3.10.1; extra == 'runtime'
|
|
22
23
|
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.25.0; extra == 'runtime'
|
|
23
24
|
Requires-Dist: uuid-utils>=0.10.0; extra == 'runtime'
|
|
25
|
+
Provides-Extra: runtime-openai
|
|
26
|
+
Requires-Dist: databricks-agents>=1.9.3; extra == 'runtime-openai'
|
|
27
|
+
Requires-Dist: databricks-openai>=0.13.0; extra == 'runtime-openai'
|
|
28
|
+
Requires-Dist: fastapi>=0.129.0; extra == 'runtime-openai'
|
|
29
|
+
Requires-Dist: mlflow>=3.10.1; extra == 'runtime-openai'
|
|
30
|
+
Requires-Dist: openai-agents>=0.4.1; extra == 'runtime-openai'
|
|
31
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.25.0; extra == 'runtime-openai'
|
|
32
|
+
Requires-Dist: uuid-utils>=0.10.0; extra == 'runtime-openai'
|
|
24
33
|
Provides-Extra: tracing
|
|
25
34
|
Requires-Dist: mlflow[databricks]>=3.9.0; extra == 'tracing'
|
|
26
35
|
Description-Content-Type: text/markdown
|
|
@@ -102,8 +111,11 @@ accessors (`store.name`) while remaining plain dicts underneath — so `store["n
|
|
|
102
111
|
mason [-p <profile>] [-o text|json]
|
|
103
112
|
login [--profile P]
|
|
104
113
|
logout
|
|
105
|
-
init [--framework openai|langgraph] [--
|
|
114
|
+
init [--framework openai|langgraph] [--disable-chat-app]
|
|
106
115
|
[--profile P] [--repo URL] [--ref REF] [directory]
|
|
116
|
+
dev [--source PATH] [--prepare-environment] [--app-port PORT]
|
|
117
|
+
[--memory/-m N] [--session/-s N]
|
|
118
|
+
[--with-traces C.S] [--no-create-stores]
|
|
107
119
|
memory
|
|
108
120
|
stores create | list | get | update | delete
|
|
109
121
|
entries create | get | list | search | update | delete
|
|
@@ -115,19 +127,39 @@ mason [-p <profile>] [-o text|json]
|
|
|
115
127
|
list | get | instrument
|
|
116
128
|
mcp
|
|
117
129
|
list [--schema CATALOG.SCHEMA]
|
|
118
|
-
init [--framework openai|langgraph] [--profile P] [DIRECTORY]
|
|
119
130
|
tools
|
|
120
131
|
add sandbox --scope SCOPE [--scope SCOPE ...] [--source PATH]
|
|
121
132
|
add mcp SERVICE [--name NAME] [--source PATH]
|
|
122
133
|
add uc-function FUNCTION [--name NAME] [--source PATH]
|
|
123
134
|
add python NAME [--source PATH]
|
|
124
135
|
list [--source PATH]
|
|
125
|
-
deploy <name> --source PATH [--
|
|
126
|
-
[--
|
|
127
|
-
[--with-traces C.S] [--create-stores]
|
|
136
|
+
deploy <name> --source PATH [--memory/-m N]
|
|
137
|
+
[--session/-s N] [--actor-id ID]
|
|
138
|
+
[--with-traces C.S] [--no-create-stores]
|
|
128
139
|
deployments list | get | logs | start | stop | delete
|
|
129
140
|
```
|
|
130
141
|
|
|
142
|
+
## Command help
|
|
143
|
+
|
|
144
|
+
Use the conventional help flag at any command level. Every command's help includes runnable
|
|
145
|
+
examples:
|
|
146
|
+
|
|
147
|
+
```sh
|
|
148
|
+
mason --help
|
|
149
|
+
mason deploy --help
|
|
150
|
+
mason sessions items append --help
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
For the shortest path from a blank directory to a running and deployed agent:
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
mason login --profile my-workspace
|
|
157
|
+
mason init my-agent
|
|
158
|
+
cd my-agent
|
|
159
|
+
mason dev
|
|
160
|
+
mason deploy my-agent
|
|
161
|
+
```
|
|
162
|
+
|
|
131
163
|
## Agent tools
|
|
132
164
|
|
|
133
165
|
`mason init` writes portable tool intent to `agent.toml` and template provenance to
|
|
@@ -168,49 +200,39 @@ arguments controlled by the model.
|
|
|
168
200
|
|
|
169
201
|
## Initialize the chat app demo
|
|
170
202
|
|
|
171
|
-
The chat app is a LangGraph-specific init overlay, not a command that mutates an existing project
|
|
203
|
+
The chat app is a LangGraph-specific init overlay, not a command that mutates an existing project.
|
|
204
|
+
It is included by default for `--framework langgraph`; pass `--disable-chat-app` to scaffold the
|
|
205
|
+
API-only backend instead.
|
|
172
206
|
|
|
173
207
|
```sh
|
|
174
|
-
mason init --framework langgraph
|
|
208
|
+
mason init --framework langgraph \
|
|
175
209
|
--profile <profile> \
|
|
176
210
|
./my-agent
|
|
177
211
|
cd ./my-agent
|
|
178
212
|
uv run start-server
|
|
179
213
|
```
|
|
180
214
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
`agent/mason/long_running.py`; the framework-specific overlay only adds `ui/`, `runtime/ui.py`, the
|
|
185
|
-
UI-enabled `runtime/main.py`, and UI tests.
|
|
215
|
+
The chat app includes synchronous, SSE streaming, background polling, Session Store, Memory Store,
|
|
216
|
+
and HITL resume UI. The framework-specific overlay adds `ui/`, `runtime/ui.py`, the UI-enabled
|
|
217
|
+
`runtime/main.py`, and UI tests.
|
|
186
218
|
|
|
187
219
|
For the full deployed demo, connect both managed stores:
|
|
188
220
|
|
|
189
221
|
```sh
|
|
190
222
|
mason --profile <profile> deploy mason-agent-demo --source . \
|
|
191
|
-
--
|
|
192
|
-
--
|
|
193
|
-
--actor-id alice
|
|
194
|
-
--create-stores
|
|
223
|
+
--session mason-demo-sessions \
|
|
224
|
+
--memory mason-demo-memory \
|
|
225
|
+
--actor-id alice
|
|
195
226
|
```
|
|
196
227
|
|
|
228
|
+
(Missing stores are created automatically; pass `--no-create-stores` to require they already exist.)
|
|
229
|
+
|
|
197
230
|
The Databricks Apps `__Host-databricks-app-router` cookie is both the sticky routing key and the
|
|
198
231
|
application session id. The browser sends it automatically; API clients must reuse it as a cookie.
|
|
199
232
|
Request bodies never carry `session_id`. A localhost-only `mason-local-session` cookie provides the
|
|
200
233
|
same behavior outside Databricks Apps. TODO: move to `X-Routing-Key` when Apps supports it.
|
|
201
234
|
|
|
202
235
|
The generated `README.md` documents every request the client makes: config discovery, sync and SSE
|
|
203
|
-
invocations, background submission and polling, session transcript loading, HITL resume, memory
|
|
204
|
-
entry operations
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
Start App runs `tool_step_1` through `tool_step_4` in a checkpointed sequence. Each completed output
|
|
208
|
-
is committed before the next node. Stop App schedules `os._exit(86)`; Databricks Apps restarts the
|
|
209
|
-
process, the browser waits for a new instance and a stale heartbeat, and then starts a new attempt
|
|
210
|
-
with the same routing cookie. Completed tools are restored and skipped; an interrupted tool whose
|
|
211
|
-
output was not committed can run again.
|
|
212
|
-
|
|
213
|
-
The ownership log is intentionally demo-grade: Session Store records append-only attempts and
|
|
214
|
-
heartbeats, but the claim is last-writer-wins rather than atomic (`atomic_claim: false`). Production
|
|
215
|
-
durability also needs transactional ownership, server-side stale scanning, idempotent side effects,
|
|
216
|
-
and durable event replay.
|
|
236
|
+
invocations, background submission and polling, session transcript loading, HITL resume, and memory
|
|
237
|
+
entry operations. Capability colors are automatic from `/api/demo/config`; only the
|
|
238
|
+
sync/streaming/background transport selector is manual.
|
|
@@ -75,8 +75,11 @@ accessors (`store.name`) while remaining plain dicts underneath — so `store["n
|
|
|
75
75
|
mason [-p <profile>] [-o text|json]
|
|
76
76
|
login [--profile P]
|
|
77
77
|
logout
|
|
78
|
-
init [--framework openai|langgraph] [--
|
|
78
|
+
init [--framework openai|langgraph] [--disable-chat-app]
|
|
79
79
|
[--profile P] [--repo URL] [--ref REF] [directory]
|
|
80
|
+
dev [--source PATH] [--prepare-environment] [--app-port PORT]
|
|
81
|
+
[--memory/-m N] [--session/-s N]
|
|
82
|
+
[--with-traces C.S] [--no-create-stores]
|
|
80
83
|
memory
|
|
81
84
|
stores create | list | get | update | delete
|
|
82
85
|
entries create | get | list | search | update | delete
|
|
@@ -88,19 +91,39 @@ mason [-p <profile>] [-o text|json]
|
|
|
88
91
|
list | get | instrument
|
|
89
92
|
mcp
|
|
90
93
|
list [--schema CATALOG.SCHEMA]
|
|
91
|
-
init [--framework openai|langgraph] [--profile P] [DIRECTORY]
|
|
92
94
|
tools
|
|
93
95
|
add sandbox --scope SCOPE [--scope SCOPE ...] [--source PATH]
|
|
94
96
|
add mcp SERVICE [--name NAME] [--source PATH]
|
|
95
97
|
add uc-function FUNCTION [--name NAME] [--source PATH]
|
|
96
98
|
add python NAME [--source PATH]
|
|
97
99
|
list [--source PATH]
|
|
98
|
-
deploy <name> --source PATH [--
|
|
99
|
-
[--
|
|
100
|
-
[--with-traces C.S] [--create-stores]
|
|
100
|
+
deploy <name> --source PATH [--memory/-m N]
|
|
101
|
+
[--session/-s N] [--actor-id ID]
|
|
102
|
+
[--with-traces C.S] [--no-create-stores]
|
|
101
103
|
deployments list | get | logs | start | stop | delete
|
|
102
104
|
```
|
|
103
105
|
|
|
106
|
+
## Command help
|
|
107
|
+
|
|
108
|
+
Use the conventional help flag at any command level. Every command's help includes runnable
|
|
109
|
+
examples:
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
mason --help
|
|
113
|
+
mason deploy --help
|
|
114
|
+
mason sessions items append --help
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
For the shortest path from a blank directory to a running and deployed agent:
|
|
118
|
+
|
|
119
|
+
```sh
|
|
120
|
+
mason login --profile my-workspace
|
|
121
|
+
mason init my-agent
|
|
122
|
+
cd my-agent
|
|
123
|
+
mason dev
|
|
124
|
+
mason deploy my-agent
|
|
125
|
+
```
|
|
126
|
+
|
|
104
127
|
## Agent tools
|
|
105
128
|
|
|
106
129
|
`mason init` writes portable tool intent to `agent.toml` and template provenance to
|
|
@@ -141,49 +164,39 @@ arguments controlled by the model.
|
|
|
141
164
|
|
|
142
165
|
## Initialize the chat app demo
|
|
143
166
|
|
|
144
|
-
The chat app is a LangGraph-specific init overlay, not a command that mutates an existing project
|
|
167
|
+
The chat app is a LangGraph-specific init overlay, not a command that mutates an existing project.
|
|
168
|
+
It is included by default for `--framework langgraph`; pass `--disable-chat-app` to scaffold the
|
|
169
|
+
API-only backend instead.
|
|
145
170
|
|
|
146
171
|
```sh
|
|
147
|
-
mason init --framework langgraph
|
|
172
|
+
mason init --framework langgraph \
|
|
148
173
|
--profile <profile> \
|
|
149
174
|
./my-agent
|
|
150
175
|
cd ./my-agent
|
|
151
176
|
uv run start-server
|
|
152
177
|
```
|
|
153
178
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
`agent/mason/long_running.py`; the framework-specific overlay only adds `ui/`, `runtime/ui.py`, the
|
|
158
|
-
UI-enabled `runtime/main.py`, and UI tests.
|
|
179
|
+
The chat app includes synchronous, SSE streaming, background polling, Session Store, Memory Store,
|
|
180
|
+
and HITL resume UI. The framework-specific overlay adds `ui/`, `runtime/ui.py`, the UI-enabled
|
|
181
|
+
`runtime/main.py`, and UI tests.
|
|
159
182
|
|
|
160
183
|
For the full deployed demo, connect both managed stores:
|
|
161
184
|
|
|
162
185
|
```sh
|
|
163
186
|
mason --profile <profile> deploy mason-agent-demo --source . \
|
|
164
|
-
--
|
|
165
|
-
--
|
|
166
|
-
--actor-id alice
|
|
167
|
-
--create-stores
|
|
187
|
+
--session mason-demo-sessions \
|
|
188
|
+
--memory mason-demo-memory \
|
|
189
|
+
--actor-id alice
|
|
168
190
|
```
|
|
169
191
|
|
|
192
|
+
(Missing stores are created automatically; pass `--no-create-stores` to require they already exist.)
|
|
193
|
+
|
|
170
194
|
The Databricks Apps `__Host-databricks-app-router` cookie is both the sticky routing key and the
|
|
171
195
|
application session id. The browser sends it automatically; API clients must reuse it as a cookie.
|
|
172
196
|
Request bodies never carry `session_id`. A localhost-only `mason-local-session` cookie provides the
|
|
173
197
|
same behavior outside Databricks Apps. TODO: move to `X-Routing-Key` when Apps supports it.
|
|
174
198
|
|
|
175
199
|
The generated `README.md` documents every request the client makes: config discovery, sync and SSE
|
|
176
|
-
invocations, background submission and polling, session transcript loading, HITL resume, memory
|
|
177
|
-
entry operations
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
Start App runs `tool_step_1` through `tool_step_4` in a checkpointed sequence. Each completed output
|
|
181
|
-
is committed before the next node. Stop App schedules `os._exit(86)`; Databricks Apps restarts the
|
|
182
|
-
process, the browser waits for a new instance and a stale heartbeat, and then starts a new attempt
|
|
183
|
-
with the same routing cookie. Completed tools are restored and skipped; an interrupted tool whose
|
|
184
|
-
output was not committed can run again.
|
|
185
|
-
|
|
186
|
-
The ownership log is intentionally demo-grade: Session Store records append-only attempts and
|
|
187
|
-
heartbeats, but the claim is last-writer-wins rather than atomic (`atomic_claim: false`). Production
|
|
188
|
-
durability also needs transactional ownership, server-side stale scanning, idempotent side effects,
|
|
189
|
-
and durable event replay.
|
|
200
|
+
invocations, background submission and polling, session transcript loading, HITL resume, and memory
|
|
201
|
+
entry operations. Capability colors are automatic from `/api/demo/config`; only the
|
|
202
|
+
sync/streaming/background transport selector is manual.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "databricks-mason"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2.dev0"
|
|
4
4
|
description = "Databricks integration for Mason"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name="Databricks", email="agent-feedback@databricks.com" },
|
|
@@ -10,6 +10,7 @@ requires-python = ">=3.10"
|
|
|
10
10
|
dependencies = [
|
|
11
11
|
"click>=8.1",
|
|
12
12
|
"databricks-sdk>=0.49",
|
|
13
|
+
"psycopg[binary]>=3.1",
|
|
13
14
|
"PyYAML>=6.0",
|
|
14
15
|
"rich>=13.7",
|
|
15
16
|
"tomli>=2.0",
|
|
@@ -20,9 +21,11 @@ dependencies = [
|
|
|
20
21
|
tracing = [
|
|
21
22
|
"mlflow[databricks]>=3.9.0",
|
|
22
23
|
]
|
|
23
|
-
# The agent-side runtime helpers
|
|
24
|
-
#
|
|
25
|
-
# `
|
|
24
|
+
# The agent-side runtime helpers a deployed agent imports. Kept as extras so a plain
|
|
25
|
+
# `pip install databricks-mason` (the CLI) stays light; each template depends on the extra for its
|
|
26
|
+
# framework. `runtime` = the LangGraph adapter (databricks_mason.langgraph); `runtime-openai` = the
|
|
27
|
+
# OpenAI Agents SDK adapter (databricks_mason.openai). Both carry the shared framework-neutral stack
|
|
28
|
+
# (databricks_mason.runtime); the framework SDKs differ, so an agent installs only the one it uses.
|
|
26
29
|
runtime = [
|
|
27
30
|
"databricks-langchain>=0.17.0",
|
|
28
31
|
"langgraph>=1.1.0",
|
|
@@ -34,6 +37,15 @@ runtime = [
|
|
|
34
37
|
"opentelemetry-exporter-otlp-proto-grpc>=1.25.0",
|
|
35
38
|
"databricks-agents>=1.9.3",
|
|
36
39
|
]
|
|
40
|
+
runtime-openai = [
|
|
41
|
+
"openai-agents>=0.4.1",
|
|
42
|
+
"databricks-openai>=0.13.0",
|
|
43
|
+
"fastapi>=0.129.0",
|
|
44
|
+
"mlflow>=3.10.1",
|
|
45
|
+
"uuid-utils>=0.10.0",
|
|
46
|
+
"opentelemetry-exporter-otlp-proto-grpc>=1.25.0",
|
|
47
|
+
"databricks-agents>=1.9.3",
|
|
48
|
+
]
|
|
37
49
|
|
|
38
50
|
[project.scripts]
|
|
39
51
|
mason = "databricks_mason.cli:main"
|
{databricks_mason-0.1.1.dev0 → databricks_mason-0.1.2.dev0}/src/databricks_mason/agent_project.py
RENAMED
|
@@ -291,7 +291,8 @@ class AgentProject:
|
|
|
291
291
|
except FileNotFoundError as exc:
|
|
292
292
|
raise AgentCliError(
|
|
293
293
|
f"Could not find agent.toml in {project_root}.",
|
|
294
|
-
hint="Run `mason init`
|
|
294
|
+
hint="This command needs a Mason project. Run `mason init` to create one, "
|
|
295
|
+
"or point at an existing project with --source <dir>.",
|
|
295
296
|
) from exc
|
|
296
297
|
except (OSError, ParseError) as exc:
|
|
297
298
|
raise AgentCliError(f"Could not read agent manifest at {path}: {exc}.") from exc
|
|
@@ -334,7 +335,17 @@ class AgentProject:
|
|
|
334
335
|
continue
|
|
335
336
|
if existing == spec:
|
|
336
337
|
return False
|
|
337
|
-
|
|
338
|
+
|
|
339
|
+
def _summary(s: ToolSpec) -> str:
|
|
340
|
+
src = s.source
|
|
341
|
+
return src.service or src.function or src.entrypoint or src.kind
|
|
342
|
+
|
|
343
|
+
raise AgentCliError(
|
|
344
|
+
f"Tool id {spec.id!r} already exists with a different configuration "
|
|
345
|
+
f"(existing: {_summary(existing)}; requested: {_summary(spec)}).",
|
|
346
|
+
hint="Use --name to add it under a different id, or remove the existing "
|
|
347
|
+
"tool from agent.toml first.",
|
|
348
|
+
)
|
|
338
349
|
raw_tools = self._document.get("tools")
|
|
339
350
|
if raw_tools is None:
|
|
340
351
|
raw_tools = tomlkit.aot()
|
|
@@ -10,10 +10,12 @@ from typing import Optional
|
|
|
10
10
|
|
|
11
11
|
import click
|
|
12
12
|
|
|
13
|
+
from databricks_mason import errors
|
|
13
14
|
from databricks_mason.auth import load_default_profile, login, logout
|
|
14
15
|
from databricks_mason.client import MasonClient
|
|
15
16
|
from databricks_mason.deploy import deploy, deployments
|
|
16
17
|
from databricks_mason.dev import dev
|
|
18
|
+
from databricks_mason.help import configure_help
|
|
17
19
|
from databricks_mason.init import init
|
|
18
20
|
from databricks_mason.mcp import mcp
|
|
19
21
|
from databricks_mason.memory import memory
|
|
@@ -56,6 +58,8 @@ def mason(ctx: click.Context, profile: Optional[str], output: str) -> None:
|
|
|
56
58
|
.databrickscfg profile (pass --profile / -p, run `mason login` to save a default,
|
|
57
59
|
or rely on the SDK's default resolution).
|
|
58
60
|
"""
|
|
61
|
+
# Let errors render to match the selected output mode (JSON errors for -o json).
|
|
62
|
+
errors.set_output_mode(output)
|
|
59
63
|
ctx.obj = CliContext(profile=profile or load_default_profile(), output=output)
|
|
60
64
|
|
|
61
65
|
|
|
@@ -70,6 +74,7 @@ mason.add_command(tracing)
|
|
|
70
74
|
mason.add_command(deploy)
|
|
71
75
|
mason.add_command(deployments)
|
|
72
76
|
mason.add_command(tools)
|
|
77
|
+
configure_help(mason)
|
|
73
78
|
|
|
74
79
|
|
|
75
80
|
def main() -> None:
|
|
@@ -36,15 +36,39 @@ def _as(cls: type, resp: Any) -> Any:
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def memory_store_path(name: str) -> str:
|
|
39
|
-
"""Normalize a store id or name into the `memory-stores/{id}` resource segment.
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
"""Normalize a store id or name into the `memory-stores/{id}` resource segment.
|
|
40
|
+
|
|
41
|
+
Validates client-side so an empty or wrong-typed argument raises a clear error
|
|
42
|
+
instead of building a URL like `memory-stores/` and leaking a raw ENDPOINT_NOT_FOUND.
|
|
43
|
+
"""
|
|
44
|
+
raw = (name or "").strip()
|
|
45
|
+
if raw.startswith("memory-stores/"):
|
|
46
|
+
raw = raw[len("memory-stores/") :]
|
|
47
|
+
raw = raw.strip().strip("/")
|
|
48
|
+
if not raw:
|
|
49
|
+
raise AgentCliError("A memory store id or resource name is required.")
|
|
50
|
+
if "/" in raw:
|
|
51
|
+
raise AgentCliError(f"Invalid memory store id or resource name: {name!r}")
|
|
52
|
+
return f"memory-stores/{raw}"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def session_store_path(name: str) -> str:
|
|
56
|
+
"""Normalize/validate a session store name into the `session-stores/{name}` segment."""
|
|
57
|
+
raw = (name or "").strip()
|
|
58
|
+
if raw.startswith("session-stores/"):
|
|
59
|
+
raw = raw[len("session-stores/") :]
|
|
60
|
+
raw = raw.strip().strip("/")
|
|
61
|
+
if not raw:
|
|
62
|
+
raise AgentCliError("A session store name is required.")
|
|
63
|
+
return f"session-stores/{raw}"
|
|
42
64
|
|
|
43
65
|
|
|
44
66
|
def memory_entry_path(store: str, entry: str) -> str:
|
|
45
|
-
entry = entry.strip().strip("/")
|
|
67
|
+
entry = (entry or "").strip().strip("/")
|
|
46
68
|
if entry.startswith("memory-stores/"):
|
|
47
69
|
return entry
|
|
70
|
+
if not entry:
|
|
71
|
+
raise AgentCliError("A memory entry id or resource name is required.")
|
|
48
72
|
return f"{memory_store_path(store)}/entries/{entry}"
|
|
49
73
|
|
|
50
74
|
|
|
@@ -162,6 +186,8 @@ class MasonClient:
|
|
|
162
186
|
self, name: str, display_name: Optional[str] = None, description: Optional[str] = None
|
|
163
187
|
) -> models.MemoryStore:
|
|
164
188
|
body = _query(display_name=display_name, description=description)
|
|
189
|
+
if not body:
|
|
190
|
+
raise AgentCliError("No fields to update. Provide a display name and/or description.")
|
|
165
191
|
mask = ",".join(body.keys())
|
|
166
192
|
return _as(
|
|
167
193
|
models.MemoryStore,
|
|
@@ -247,6 +273,8 @@ class MasonClient:
|
|
|
247
273
|
description: Optional[str] = None,
|
|
248
274
|
) -> models.MemoryEntry:
|
|
249
275
|
body = _query(content=content, description=description)
|
|
276
|
+
if not body:
|
|
277
|
+
raise AgentCliError("No fields to update. Provide content and/or a description.")
|
|
250
278
|
return _as(
|
|
251
279
|
models.MemoryEntry,
|
|
252
280
|
self._do("PATCH", f"{_BASE}/{memory_entry_path(store, entry)}", body=body),
|
|
@@ -269,7 +297,7 @@ class MasonClient:
|
|
|
269
297
|
)
|
|
270
298
|
|
|
271
299
|
def get_session_store(self, name: str) -> models.SessionStore:
|
|
272
|
-
return _as(models.SessionStore, self._do("GET", f"{_BASE}/
|
|
300
|
+
return _as(models.SessionStore, self._do("GET", f"{_BASE}/{session_store_path(name)}"))
|
|
273
301
|
|
|
274
302
|
def list_session_stores(
|
|
275
303
|
self, page_size: Optional[int] = None, page_token: Optional[str] = None
|
|
@@ -287,16 +315,21 @@ class MasonClient:
|
|
|
287
315
|
self, name: str, description: Optional[str] = None, metadata: Optional[dict] = None
|
|
288
316
|
) -> models.SessionStore:
|
|
289
317
|
body = _query(description=description, metadata=metadata)
|
|
318
|
+
if not body:
|
|
319
|
+
raise AgentCliError("No fields to update. Provide a description and/or metadata.")
|
|
290
320
|
mask = ",".join(body.keys())
|
|
291
321
|
return _as(
|
|
292
322
|
models.SessionStore,
|
|
293
323
|
self._do(
|
|
294
|
-
"PATCH",
|
|
324
|
+
"PATCH",
|
|
325
|
+
f"{_BASE}/{session_store_path(name)}",
|
|
326
|
+
query=_query(update_mask=mask),
|
|
327
|
+
body=body,
|
|
295
328
|
),
|
|
296
329
|
)
|
|
297
330
|
|
|
298
331
|
def delete_session_store(self, name: str) -> dict:
|
|
299
|
-
return self._do("DELETE", f"{_BASE}/
|
|
332
|
+
return self._do("DELETE", f"{_BASE}/{session_store_path(name)}")
|
|
300
333
|
|
|
301
334
|
# --- sessions ------------------------------------------------------------
|
|
302
335
|
|
|
@@ -67,11 +67,35 @@ def _app_compute_state(name: str, profile: Optional[str]) -> Optional[str]:
|
|
|
67
67
|
return None
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
def _validate_deployment_name(name: str) -> str:
|
|
71
|
+
"""Reject an empty or unsafe deployment name before it reaches a URL / workspace path."""
|
|
72
|
+
if (
|
|
73
|
+
not (name or "").strip()
|
|
74
|
+
or name != name.strip()
|
|
75
|
+
or any(token in name for token in ("/", "\\", ".."))
|
|
76
|
+
or any(character.isspace() for character in name)
|
|
77
|
+
):
|
|
78
|
+
raise AgentCliError(
|
|
79
|
+
f"Invalid deployment name {name!r}.",
|
|
80
|
+
hint="Use a non-empty name of letters, digits, and hyphens "
|
|
81
|
+
"(no slashes, spaces, or '..').",
|
|
82
|
+
)
|
|
83
|
+
return name
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _confirm_destroy(target: str, *, assume_yes: bool) -> None:
|
|
87
|
+
"""Prompt before a destructive deployment op; --yes/-y skips it (for scripts)."""
|
|
88
|
+
if assume_yes:
|
|
89
|
+
return
|
|
90
|
+
if not click.confirm(f"{target}? This cannot be undone.", default=False):
|
|
91
|
+
raise click.Abort()
|
|
92
|
+
|
|
93
|
+
|
|
70
94
|
def _wait_for_running(name: str, profile: Optional[str], timeout_s: int = 300) -> None:
|
|
71
|
-
"""Block until a just-created app's compute is
|
|
95
|
+
"""Block until a just-created app's compute is ACTIVE (or raise on timeout).
|
|
72
96
|
|
|
73
|
-
`apps create` returns before compute is provisioned, but `apps deploy` requires
|
|
74
|
-
first deploy races without this wait.
|
|
97
|
+
`apps create` returns before compute is provisioned, but `apps deploy` requires the app to be
|
|
98
|
+
ACTIVE — so a first deploy races without this wait.
|
|
75
99
|
"""
|
|
76
100
|
deadline = time.monotonic() + timeout_s
|
|
77
101
|
while time.monotonic() < deadline:
|
|
@@ -186,9 +210,10 @@ def resolve_store_env(
|
|
|
186
210
|
"""Resolve store/trace references to the AGENT_*/MLFLOW_* env vars that wire them in.
|
|
187
211
|
|
|
188
212
|
Shared by `mason deploy` and `mason dev` so both wire an agent's stores into app.yaml the same
|
|
189
|
-
way. With `create_stores
|
|
190
|
-
exist. The memory store resolves to its bare id (the runtime re-adds the
|
|
191
|
-
when building the entries URL); the session store and trace destination
|
|
213
|
+
way. With `create_stores` (the default), missing stores are created (idempotent); when it is off
|
|
214
|
+
they must already exist. The memory store resolves to its bare id (the runtime re-adds the
|
|
215
|
+
`memory-stores/` prefix when building the entries URL); the session store and trace destination
|
|
216
|
+
are used verbatim.
|
|
192
217
|
"""
|
|
193
218
|
env: dict[str, str] = {}
|
|
194
219
|
if memory_store:
|
|
@@ -200,13 +225,24 @@ def resolve_store_env(
|
|
|
200
225
|
store = _resolve_memory_store(client, memory_store)
|
|
201
226
|
if store is None:
|
|
202
227
|
raise AgentCliError(
|
|
203
|
-
f"Memory store '{memory_store}' does not exist
|
|
228
|
+
f"Memory store '{memory_store}' does not exist "
|
|
229
|
+
"(drop --no-create-stores to create it)."
|
|
204
230
|
)
|
|
205
231
|
store_name = field(store, "name") or memory_store
|
|
206
232
|
env[_MEMORY_ENV] = store_name.split("/", 1)[-1]
|
|
207
233
|
if session_store:
|
|
208
234
|
if create_stores:
|
|
209
235
|
_ensure_session_store(client, session_store)
|
|
236
|
+
else:
|
|
237
|
+
# Validate existence up front so a typo fails at deploy time, not at runtime.
|
|
238
|
+
try:
|
|
239
|
+
client.get_session_store(session_store)
|
|
240
|
+
except AgentCliError as exc:
|
|
241
|
+
raise AgentCliError(
|
|
242
|
+
f"Session store '{session_store}' does not exist "
|
|
243
|
+
"(drop --no-create-stores to create it).",
|
|
244
|
+
error_code=exc.error_code,
|
|
245
|
+
) from exc
|
|
210
246
|
env[_SESSION_ENV] = session_store
|
|
211
247
|
if traces_destination:
|
|
212
248
|
env[TRACES_DEST_ENV] = traces_destination
|
|
@@ -266,13 +302,15 @@ def _grant_store_access(
|
|
|
266
302
|
"current directory.",
|
|
267
303
|
)
|
|
268
304
|
@click.option(
|
|
269
|
-
"--
|
|
305
|
+
"--memory",
|
|
306
|
+
"-m",
|
|
270
307
|
"memory_store",
|
|
271
308
|
default=None,
|
|
272
309
|
help="Memory store display name to wire in via AGENT_MEMORY_STORE.",
|
|
273
310
|
)
|
|
274
311
|
@click.option(
|
|
275
|
-
"--
|
|
312
|
+
"--session",
|
|
313
|
+
"-s",
|
|
276
314
|
"session_store",
|
|
277
315
|
default=None,
|
|
278
316
|
help="Session store name to wire in via AGENT_SESSION_STORE.",
|
|
@@ -296,9 +334,10 @@ def _grant_store_access(
|
|
|
296
334
|
help="MLflow experiment path to wire in via MLFLOW_EXPERIMENT_NAME.",
|
|
297
335
|
)
|
|
298
336
|
@click.option(
|
|
299
|
-
"--create-stores",
|
|
337
|
+
"--no-create-stores",
|
|
300
338
|
is_flag=True,
|
|
301
|
-
help="
|
|
339
|
+
help="Require referenced stores to already exist. By default missing stores are created "
|
|
340
|
+
"(idempotent).",
|
|
302
341
|
)
|
|
303
342
|
@click.option(
|
|
304
343
|
"--pip-index-url",
|
|
@@ -323,11 +362,12 @@ def deploy(
|
|
|
323
362
|
actor_id,
|
|
324
363
|
traces_destination,
|
|
325
364
|
traces_experiment,
|
|
326
|
-
|
|
365
|
+
no_create_stores,
|
|
327
366
|
pip_index_url,
|
|
328
367
|
workspace_path,
|
|
329
368
|
) -> None:
|
|
330
369
|
"""Deploy an agent: provision its stores, wire them in, and roll out the deployment."""
|
|
370
|
+
_validate_deployment_name(name)
|
|
331
371
|
source_dir = pathlib.Path(source)
|
|
332
372
|
client = obj.client()
|
|
333
373
|
|
|
@@ -339,7 +379,7 @@ def deploy(
|
|
|
339
379
|
session_store=session_store,
|
|
340
380
|
traces_destination=traces_destination,
|
|
341
381
|
traces_experiment=traces_experiment,
|
|
342
|
-
create_stores=
|
|
382
|
+
create_stores=not no_create_stores,
|
|
343
383
|
)
|
|
344
384
|
provisioned: dict[str, Any] = {}
|
|
345
385
|
if _MEMORY_ENV in env_updates:
|
|
@@ -411,7 +451,7 @@ def deploy(
|
|
|
411
451
|
steps.insert(
|
|
412
452
|
0,
|
|
413
453
|
"The app's service principal needs read/write on its store tables; that grant couldn't "
|
|
414
|
-
"be applied automatically (it requires store ownership
|
|
454
|
+
"be applied automatically (it requires store ownership). "
|
|
415
455
|
f"Cause: {grant_error}",
|
|
416
456
|
)
|
|
417
457
|
if grants_stores and grant_error is None:
|
|
@@ -470,6 +510,7 @@ def deployments_list(obj) -> None:
|
|
|
470
510
|
@click.pass_obj
|
|
471
511
|
def deployments_get(obj, name) -> None:
|
|
472
512
|
"""Get an agent deployment's details."""
|
|
513
|
+
_validate_deployment_name(name)
|
|
473
514
|
result = _databricks(["apps", "get", name, "-o", "json"], obj.profile, capture=True)
|
|
474
515
|
data = json.loads(result.stdout or "{}")
|
|
475
516
|
if obj.output == "json":
|
|
@@ -495,6 +536,7 @@ def deployments_get(obj, name) -> None:
|
|
|
495
536
|
@click.pass_obj
|
|
496
537
|
def deployments_logs(obj, name) -> None:
|
|
497
538
|
"""Stream a deployment's logs."""
|
|
539
|
+
_validate_deployment_name(name)
|
|
498
540
|
_databricks(["apps", "logs", name], obj.profile)
|
|
499
541
|
|
|
500
542
|
|
|
@@ -503,6 +545,7 @@ def deployments_logs(obj, name) -> None:
|
|
|
503
545
|
@click.pass_obj
|
|
504
546
|
def deployments_start(obj, name) -> None:
|
|
505
547
|
"""Start a deployment."""
|
|
548
|
+
_validate_deployment_name(name)
|
|
506
549
|
_databricks(["apps", "start", name], obj.profile)
|
|
507
550
|
if obj.output == "json":
|
|
508
551
|
render.emit_json({"started": name})
|
|
@@ -512,9 +555,12 @@ def deployments_start(obj, name) -> None:
|
|
|
512
555
|
|
|
513
556
|
@deployments.command("stop")
|
|
514
557
|
@click.argument("name")
|
|
558
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip the confirmation prompt.")
|
|
515
559
|
@click.pass_obj
|
|
516
|
-
def deployments_stop(obj, name) -> None:
|
|
560
|
+
def deployments_stop(obj, name, yes) -> None:
|
|
517
561
|
"""Stop a deployment."""
|
|
562
|
+
_validate_deployment_name(name)
|
|
563
|
+
_confirm_destroy(f"Stop deployment '{name}'", assume_yes=yes)
|
|
518
564
|
_databricks(["apps", "stop", name], obj.profile)
|
|
519
565
|
if obj.output == "json":
|
|
520
566
|
render.emit_json({"stopped": name})
|
|
@@ -524,9 +570,12 @@ def deployments_stop(obj, name) -> None:
|
|
|
524
570
|
|
|
525
571
|
@deployments.command("delete")
|
|
526
572
|
@click.argument("name")
|
|
573
|
+
@click.option("--yes", "-y", is_flag=True, help="Skip the confirmation prompt.")
|
|
527
574
|
@click.pass_obj
|
|
528
|
-
def deployments_delete(obj, name) -> None:
|
|
575
|
+
def deployments_delete(obj, name, yes) -> None:
|
|
529
576
|
"""Delete a deployment."""
|
|
577
|
+
_validate_deployment_name(name)
|
|
578
|
+
_confirm_destroy(f"Delete deployment '{name}'", assume_yes=yes)
|
|
530
579
|
_databricks(["apps", "delete", name], obj.profile)
|
|
531
580
|
if obj.output == "json":
|
|
532
581
|
render.emit_json({"deleted": name})
|