databricks-agentbricks 0.2.0__py3-none-any.whl
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_agentbricks/__init__.py +1 -0
- databricks_agentbricks/_group.py +217 -0
- databricks_agentbricks/agent_project.py +657 -0
- databricks_agentbricks/app_resources.py +139 -0
- databricks_agentbricks/cli/__init__.py +12 -0
- databricks_agentbricks/cli/app.py +108 -0
- databricks_agentbricks/cli/app_auth.py +198 -0
- databricks_agentbricks/cli/auth.py +142 -0
- databricks_agentbricks/cli/deploy.py +954 -0
- databricks_agentbricks/cli/dev.py +310 -0
- databricks_agentbricks/cli/endpoint.py +145 -0
- databricks_agentbricks/cli/endpoint_examples.py +33 -0
- databricks_agentbricks/cli/endpoint_output.py +53 -0
- databricks_agentbricks/cli/endpoint_request.py +71 -0
- databricks_agentbricks/cli/endpoint_transport.py +148 -0
- databricks_agentbricks/cli/help.py +467 -0
- databricks_agentbricks/cli/init.py +496 -0
- databricks_agentbricks/cli/mcp.py +75 -0
- databricks_agentbricks/cli/memory.py +520 -0
- databricks_agentbricks/cli/sandbox.py +823 -0
- databricks_agentbricks/cli/sessions.py +588 -0
- databricks_agentbricks/cli/tools.py +456 -0
- databricks_agentbricks/cli/tracing.py +724 -0
- databricks_agentbricks/databricks_cli.py +38 -0
- databricks_agentbricks/errors.py +114 -0
- databricks_agentbricks/lakebase_runtime_store.py +107 -0
- databricks_agentbricks/legacy_lakebase_runtime_store.py +74 -0
- databricks_agentbricks/project_config.py +157 -0
- databricks_agentbricks/project_types.py +55 -0
- databricks_agentbricks/py.typed +0 -0
- databricks_agentbricks/render.py +349 -0
- databricks_agentbricks/templates/agent-bricks-migrate/SKILL.md +101 -0
- databricks_agentbricks/templates/agent-langgraph/.env.example +36 -0
- databricks_agentbricks/templates/agent-langgraph/.gitignore +199 -0
- databricks_agentbricks/templates/agent-langgraph/AGENTKIT_CONTRACT.md +95 -0
- databricks_agentbricks/templates/agent-langgraph/AGENTS.md +31 -0
- databricks_agentbricks/templates/agent-langgraph/CLAUDE.md +1 -0
- databricks_agentbricks/templates/agent-langgraph/README.md +141 -0
- databricks_agentbricks/templates/agent-langgraph/agent/__init__.py +0 -0
- databricks_agentbricks/templates/agent-langgraph/agent/agent.py +177 -0
- databricks_agentbricks/templates/agent-langgraph/agent/mcps.py +27 -0
- databricks_agentbricks/templates/agent-langgraph/agent/tools/__init__.py +24 -0
- databricks_agentbricks/templates/agent-langgraph/agent/tools/sample_tool.py +15 -0
- databricks_agentbricks/templates/agent-langgraph/agent/tools/send_message.py +17 -0
- databricks_agentbricks/templates/agent-langgraph/app.yaml +15 -0
- databricks_agentbricks/templates/agent-langgraph/pyproject.toml +42 -0
- databricks_agentbricks/templates/agent-langgraph/runtime/__init__.py +0 -0
- databricks_agentbricks/templates/agent-langgraph/runtime/adapter.py +144 -0
- databricks_agentbricks/templates/agent-langgraph/runtime/main.py +25 -0
- databricks_agentbricks/templates/agent-langgraph/tests/__init__.py +0 -0
- databricks_agentbricks/templates/agent-langgraph/tests/test_agent.py +211 -0
- databricks_agentbricks/templates/agent-langgraph/tests/test_runtime.py +19 -0
- databricks_agentbricks/templates/agent-langgraph/tests/test_workspace.py +28 -0
- databricks_agentbricks/templates/agent-openai/.env.example +37 -0
- databricks_agentbricks/templates/agent-openai/.gitignore +199 -0
- databricks_agentbricks/templates/agent-openai/AGENTKIT_CONTRACT.md +107 -0
- databricks_agentbricks/templates/agent-openai/AGENTS.md +76 -0
- databricks_agentbricks/templates/agent-openai/CLAUDE.md +1 -0
- databricks_agentbricks/templates/agent-openai/README.md +143 -0
- databricks_agentbricks/templates/agent-openai/agent/__init__.py +0 -0
- databricks_agentbricks/templates/agent-openai/agent/agent.py +205 -0
- databricks_agentbricks/templates/agent-openai/agent/mcps.py +28 -0
- databricks_agentbricks/templates/agent-openai/agent/tools/__init__.py +24 -0
- databricks_agentbricks/templates/agent-openai/agent/tools/sample_tool.py +15 -0
- databricks_agentbricks/templates/agent-openai/agent/tools/send_message.py +18 -0
- databricks_agentbricks/templates/agent-openai/app.yaml +15 -0
- databricks_agentbricks/templates/agent-openai/pyproject.toml +42 -0
- databricks_agentbricks/templates/agent-openai/runtime/__init__.py +0 -0
- databricks_agentbricks/templates/agent-openai/runtime/adapter.py +188 -0
- databricks_agentbricks/templates/agent-openai/runtime/main.py +25 -0
- databricks_agentbricks/templates/agent-openai/tests/__init__.py +0 -0
- databricks_agentbricks/templates/agent-openai/tests/test_agent.py +326 -0
- databricks_agentbricks/templates/agent-openai/tests/test_runtime.py +19 -0
- databricks_agentbricks/templates/agent-openai/tests/test_workspace.py +28 -0
- databricks_agentbricks/templates/custom-agent-langgraph/.env.example +1 -0
- databricks_agentbricks/templates/custom-agent-langgraph/.gitignore +11 -0
- databricks_agentbricks/templates/custom-agent-langgraph/README.md +22 -0
- databricks_agentbricks/templates/custom-agent-langgraph/agent/__init__.py +0 -0
- databricks_agentbricks/templates/custom-agent-langgraph/agent/agent.py +27 -0
- databricks_agentbricks/templates/custom-agent-langgraph/app.yaml +1 -0
- databricks_agentbricks/templates/custom-agent-langgraph/pyproject.toml +38 -0
- databricks_agentbricks/templates/custom-agent-langgraph/runtime/__init__.py +0 -0
- databricks_agentbricks/templates/custom-agent-langgraph/runtime/main.py +31 -0
- databricks_agentbricks/templates/custom-agent-langgraph/tests/test_runtime.py +29 -0
- databricks_agentbricks/templates/custom-agent-openai/.env.example +1 -0
- databricks_agentbricks/templates/custom-agent-openai/.gitignore +11 -0
- databricks_agentbricks/templates/custom-agent-openai/README.md +22 -0
- databricks_agentbricks/templates/custom-agent-openai/agent/__init__.py +0 -0
- databricks_agentbricks/templates/custom-agent-openai/agent/agent.py +29 -0
- databricks_agentbricks/templates/custom-agent-openai/app.yaml +1 -0
- databricks_agentbricks/templates/custom-agent-openai/pyproject.toml +38 -0
- databricks_agentbricks/templates/custom-agent-openai/runtime/__init__.py +0 -0
- databricks_agentbricks/templates/custom-agent-openai/runtime/main.py +31 -0
- databricks_agentbricks/templates/custom-agent-openai/tests/test_runtime.py +29 -0
- databricks_agentbricks/templates/sandbox_mcp.py +39 -0
- databricks_agentbricks/templates/sandbox_mcp_langgraph.py +39 -0
- databricks_agentbricks/templates/ui/agent-langgraph/CHAT_APP.md +49 -0
- databricks_agentbricks/templates/ui/agent-langgraph/runtime/main.py +26 -0
- databricks_agentbricks/templates/ui/agent-langgraph/runtime/ui.py +528 -0
- databricks_agentbricks/templates/ui/agent-langgraph/tests/test_demo_ui.py +542 -0
- databricks_agentbricks/templates/ui/agent-langgraph/ui/agentbricks-mark.png +0 -0
- databricks_agentbricks/templates/ui/agent-langgraph/ui/app.js +1464 -0
- databricks_agentbricks/templates/ui/agent-langgraph/ui/index.html +281 -0
- databricks_agentbricks/templates/ui/agent-langgraph/ui/styles.css +1913 -0
- databricks_agentbricks/templates/ui/agent-openai/CHAT_APP.md +54 -0
- databricks_agentbricks/templates/ui/agent-openai/runtime/main.py +26 -0
- databricks_agentbricks/templates/ui/agent-openai/runtime/ui.py +521 -0
- databricks_agentbricks/templates/ui/agent-openai/tests/test_demo_ui.py +512 -0
- databricks_agentbricks/templates/ui/agent-openai/ui/agentbricks-mark.png +0 -0
- databricks_agentbricks/templates/ui/agent-openai/ui/app.js +1428 -0
- databricks_agentbricks/templates/ui/agent-openai/ui/index.html +281 -0
- databricks_agentbricks/templates/ui/agent-openai/ui/styles.css +1913 -0
- databricks_agentbricks/theme.py +72 -0
- databricks_agentbricks-0.2.0.dist-info/METADATA +897 -0
- databricks_agentbricks-0.2.0.dist-info/RECORD +159 -0
- databricks_agentbricks-0.2.0.dist-info/WHEEL +4 -0
- databricks_agentbricks-0.2.0.dist-info/entry_points.txt +2 -0
- databricks_agentbricks-0.2.0.dist-info/licenses/NOTICE +39 -0
- databricks_agentkit/__init__.py +67 -0
- databricks_agentkit/_api_client.py +673 -0
- databricks_agentkit/_pagination.py +25 -0
- databricks_agentkit/client.py +31 -0
- databricks_agentkit/langgraph/__init__.py +98 -0
- databricks_agentkit/langgraph/genie.py +50 -0
- databricks_agentkit/langgraph/mcp.py +216 -0
- databricks_agentkit/langgraph/memory.py +67 -0
- databricks_agentkit/langgraph/session_store.py +485 -0
- databricks_agentkit/memory_store.py +371 -0
- databricks_agentkit/models.py +330 -0
- databricks_agentkit/openai/__init__.py +93 -0
- databricks_agentkit/openai/genie.py +50 -0
- databricks_agentkit/openai/mcp.py +189 -0
- databricks_agentkit/openai/memory.py +67 -0
- databricks_agentkit/openai/sessions.py +157 -0
- databricks_agentkit/py.typed +0 -0
- databricks_agentkit/runtime/ARCHITECTURE.md +64 -0
- databricks_agentkit/runtime/README.md +184 -0
- databricks_agentkit/runtime/__init__.py +69 -0
- databricks_agentkit/runtime/app.py +316 -0
- databricks_agentkit/runtime/auth.py +149 -0
- databricks_agentkit/runtime/durability/__init__.py +1 -0
- databricks_agentkit/runtime/durability/execution.py +133 -0
- databricks_agentkit/runtime/durability/heartbeat.py +64 -0
- databricks_agentkit/runtime/durability/lakebase_runtime_store.py +648 -0
- databricks_agentkit/runtime/durability/recovery.py +70 -0
- databricks_agentkit/runtime/durability/store.py +33 -0
- databricks_agentkit/runtime/execution.py +160 -0
- databricks_agentkit/runtime/genie.py +224 -0
- databricks_agentkit/runtime/mcp_auth.py +114 -0
- databricks_agentkit/runtime/model_services.py +90 -0
- databricks_agentkit/runtime/runtime.py +273 -0
- databricks_agentkit/runtime/session_store_client.py +120 -0
- databricks_agentkit/runtime/store.py +287 -0
- databricks_agentkit/runtime/tool_manifest.py +223 -0
- databricks_agentkit/runtime/tracing.py +71 -0
- databricks_agentkit/runtime/types.py +118 -0
- databricks_agentkit/runtime/workspace.py +27 -0
- databricks_agentkit/session_store.py +434 -0
- databricks_agentkit/timefmt.py +85 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Managed runtime integration contract for OpenAI Agents
|
|
2
|
+
|
|
3
|
+
This document owns the requirements shared by generated projects and existing agents adopting
|
|
4
|
+
the managed runtime. The [README](README.md) owns commands and HTTP examples; adapter docstrings own API details.
|
|
5
|
+
The adjacent template is an example, not a required agent architecture. Runtime internals live in
|
|
6
|
+
the installed package's runtime guide.
|
|
7
|
+
|
|
8
|
+
## Command requirements
|
|
9
|
+
|
|
10
|
+
Agent Bricks CLI commands operate on an application directory; they do not rewrite its agent. Each capability
|
|
11
|
+
needs runtime wiring as well as manifest configuration.
|
|
12
|
+
|
|
13
|
+
| Command | Required integration |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| `dev`, `deploy` | Installable dependencies and an `app.yaml` command starting the application |
|
|
16
|
+
| `tools add mcp`, `uc-function`, `genie`, `sandbox` | Load bindings into agent tool lists and tool execution |
|
|
17
|
+
| `sessions bind` | Select the bound session store and supply session/actor identity |
|
|
18
|
+
| `memory bind` | Include bound, actor-scoped memory tools in the agent tool list |
|
|
19
|
+
| `tracing bind`, `tracing unbind` | Initialize tracing and open a root span from resolved config |
|
|
20
|
+
| `endpoint invoke` | An HTTP endpoint; the caller supplies its path and complete request body |
|
|
21
|
+
|
|
22
|
+
### Project and startup
|
|
23
|
+
|
|
24
|
+
Use `agent.toml` for framework, tool/store bindings, and tracing. The existing `.agentbricks/project.toml`
|
|
25
|
+
file records framework and template provenance; keep it when working with projects that have it.
|
|
26
|
+
Metadata alone does not integrate adapters. Install a compatible `databricks-agentbricks[openai]`
|
|
27
|
+
distribution, supply the real command in `app.yaml`, load configuration before adapters, and listen
|
|
28
|
+
on the app port. The Agent Bricks CLI and runtime find `agent.toml` from the working directory or
|
|
29
|
+
`AGENTBRICKS_PROJECT_ROOT`. Keep credentials out of `app.yaml`.
|
|
30
|
+
|
|
31
|
+
Store binding commands declare intent. `dev` and `deploy` resolve or provision declared stores and
|
|
32
|
+
supply runtime config; deploy grants app access. The resolved Memory Store ID reaches the runtime
|
|
33
|
+
as `AGENT_MEMORY_STORE`, rather than being stored as an ID in the manifest. Do not hardcode values
|
|
34
|
+
that make later bindings ineffective.
|
|
35
|
+
|
|
36
|
+
The model provider is a choice, not a requirement. The template's [agent/agent.py](agent/agent.py)
|
|
37
|
+
wires the Databricks Unity Catalog AI Gateway (`AsyncDatabricksOpenAI(..., use_ai_gateway=True)`
|
|
38
|
+
plus a `system.ai.*` model), but an agent may keep its existing provider instead, e.g. direct
|
|
39
|
+
OpenAI via `OPENAI_API_KEY` and a model string like `gpt-4o-mini`. Whichever is chosen,
|
|
40
|
+
`configure()` sets it up at startup, and deployment must ensure the chosen model is reachable
|
|
41
|
+
from the app.
|
|
42
|
+
|
|
43
|
+
### Tools
|
|
44
|
+
|
|
45
|
+
Use `databricks_agentkit.openai.mcp_servers()` for MCP, UC function, and sandbox bindings, combining
|
|
46
|
+
them with agent-owned servers without duplicates. It applies sandbox downscoping. Genie Agent
|
|
47
|
+
bindings arrive as tools from `genie_tools()`. Local Python tools belong in source; the template
|
|
48
|
+
discovers `@function_tool` functions through [agent/tools/__init__.py](agent/tools/__init__.py).
|
|
49
|
+
|
|
50
|
+
Feed selected tools into the agent's tool list while preserving approval policy. Tools that gate on
|
|
51
|
+
approval declare `needs_approval=True` and appear in the template's `REQUIRE_APPROVAL` set so
|
|
52
|
+
pending calls surface as interrupts. New bindings must become effective on the next agent
|
|
53
|
+
construction or restart without another source edit.
|
|
54
|
+
|
|
55
|
+
### Sessions and memory
|
|
56
|
+
|
|
57
|
+
Pass `session_store(session_id)` as the `session` argument of `Runner.run(agent, messages,
|
|
58
|
+
session=...)` for managed conversation state. The helper selects the bound Session Store or
|
|
59
|
+
an in-process session; explicit arguments and environment overrides take precedence. Supply the
|
|
60
|
+
actor so the durable store partitions transcripts per user. It caches the in-process session per
|
|
61
|
+
process, so restart after a binding change.
|
|
62
|
+
|
|
63
|
+
Include `memory_tools(actor)` in the agent's tool list. It returns no tools when unconfigured.
|
|
64
|
+
Its closures capture actor identity, so never reuse them across users. The application owns
|
|
65
|
+
trusted actor/tenant authentication; a payload field alone does not establish identity.
|
|
66
|
+
|
|
67
|
+
### Tracing
|
|
68
|
+
|
|
69
|
+
Call `configure_tracing()` after loading config; it binds `mlflow.openai.autolog`. Then wrap agent
|
|
70
|
+
execution, including streams, in `start_trace(..., session_id=session_id)`. The root span lets
|
|
71
|
+
framework autologging record children. The helper enables tracing when destination and experiment
|
|
72
|
+
settings are present and disables it otherwise. Preserve existing tracing semantics when composing
|
|
73
|
+
it.
|
|
74
|
+
|
|
75
|
+
## Managed server adapter
|
|
76
|
+
|
|
77
|
+
For the managed invocation protocol, construct `DurableAgentServer` in [runtime/main.py](runtime/main.py)
|
|
78
|
+
and register [runtime/adapter.py](runtime/adapter.py) hooks. Keep framework-native execution in
|
|
79
|
+
[agent/agent.py](agent/agent.py), independent of runtime request/context types.
|
|
80
|
+
|
|
81
|
+
The invoke hook translates opaque application input, runs the agent through the framework `Runner`,
|
|
82
|
+
translates native events, calls `await context.emit(event)`, and returns JSON output. DurableAgentServer owns
|
|
83
|
+
foreground/background transport, polling, and replay. Invocation UUIDs differ from stable
|
|
84
|
+
application session IDs; the routing cookie is not the application session.
|
|
85
|
+
|
|
86
|
+
The example assumes `messages` input and message, delta, and interrupt events. Custom inputs,
|
|
87
|
+
outputs, and interruptions require explicit mappings and must not be discarded to fit the example.
|
|
88
|
+
|
|
89
|
+
### Recovery and durability
|
|
90
|
+
|
|
91
|
+
Runtime Store persistence covers invocations and emitted events. Session Store persistence covers
|
|
92
|
+
the conversation transcript, not in-flight run state. The Agents SDK has no node-level checkpoint
|
|
93
|
+
continuation: OpenAI HITL `RunState` is process-local and does not survive worker loss even with a
|
|
94
|
+
Session Store bound. `dev` uses process-local invocation storage; deployment attaches a
|
|
95
|
+
Lakebase-backed Runtime Store.
|
|
96
|
+
|
|
97
|
+
Register recovery when intended. The recover hook replays the persisted application input against
|
|
98
|
+
the same session and prepends a developer instruction warning that the prior attempt may have
|
|
99
|
+
partially completed. Recovery is at least once, so side effects must be idempotent and tools must
|
|
100
|
+
tolerate replay.
|
|
101
|
+
|
|
102
|
+
## Optional chat app
|
|
103
|
+
|
|
104
|
+
No CLI command requires the chat UI. The default overlay supplies it; `--disable-chat-app` omits
|
|
105
|
+
it. When adopting it, inspect `CHAT_APP.md`, `runtime/ui.py`, and browser code. Adapt model choice,
|
|
106
|
+
history, approval interrupts, and authentication to the actual agent. Its message-oriented
|
|
107
|
+
assumptions must not silently change custom application behavior.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Agent Development Guide
|
|
2
|
+
|
|
3
|
+
This project is an OpenAI Agents SDK workload hosted by `databricks_agentkit.DurableAgentServer`.
|
|
4
|
+
|
|
5
|
+
Read [AGENTKIT_CONTRACT.md](AGENTKIT_CONTRACT.md) before changing integration points. It owns command
|
|
6
|
+
requirements, tool/state/tracing wiring, and recovery. [README.md](README.md) owns setup and client
|
|
7
|
+
examples. Keep this file as a development map rather than repeating those rules.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
ab dev
|
|
13
|
+
uv run pytest
|
|
14
|
+
ab --profile <profile> deploy <name> --source .
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Request contract
|
|
18
|
+
|
|
19
|
+
Use only `/api/invocations`. The transport body is:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"id": "<uuid>",
|
|
24
|
+
"input": {
|
|
25
|
+
"session_id": "<stable-application-session>",
|
|
26
|
+
"messages": [{"role": "user", "content": "hello"}],
|
|
27
|
+
"resume": null,
|
|
28
|
+
"model": "optional-serving-endpoint"
|
|
29
|
+
},
|
|
30
|
+
"background": false,
|
|
31
|
+
"stream": false
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`id` is the invocation identifier and idempotency key. `background` and `stream` are transport
|
|
36
|
+
fields. Everything framework-specific belongs inside `input`. The browser generates a stable
|
|
37
|
+
session ID in local storage; API clients should do the same. The Apps router cookie is only for
|
|
38
|
+
sticky routing and is not authentication or application session state.
|
|
39
|
+
|
|
40
|
+
## Code map
|
|
41
|
+
|
|
42
|
+
| Change | File |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| Framework-native agent and `run_agent` | `agent/agent.py` |
|
|
45
|
+
| Local tools | `agent/tools/` |
|
|
46
|
+
| MCP servers | `agent/mcps.py` |
|
|
47
|
+
| Managed runtime `invoke`/`recover` hooks and input/output translation | `runtime/adapter.py` |
|
|
48
|
+
| `DurableAgentServer` construction and hook registration | `runtime/main.py` |
|
|
49
|
+
| Browser and managed-state routes | `runtime/ui.py` |
|
|
50
|
+
| Browser behavior | `ui/app.js` |
|
|
51
|
+
|
|
52
|
+
Keep `agent/agent.py` runnable without runtime request or context types. If you bring an existing agent,
|
|
53
|
+
put its framework-native execution in `run_agent`. The small `runtime/adapter.py` is the agent-author
|
|
54
|
+
integration point: it translates the application payload, calls `run_agent`, emits runtime events, and
|
|
55
|
+
shapes the response. Its `recover` hook calls the same `run_agent` with the original application input
|
|
56
|
+
plus a developer instruction warning that the prior attempt may have partially completed because the
|
|
57
|
+
Agents SDK does not expose checkpoint continuation.
|
|
58
|
+
|
|
59
|
+
## State and recovery
|
|
60
|
+
|
|
61
|
+
- Invocation state/events: in-memory in `ab dev`; Lakebase when `ab deploy` attaches a Runtime
|
|
62
|
+
Store.
|
|
63
|
+
- Conversation transcript: in-process in `ab dev`; managed Session Store when bound, on `ab deploy`.
|
|
64
|
+
- Long-term memory: off in `ab dev`; managed Memory Store when bound, on `ab deploy`.
|
|
65
|
+
- OpenAI HITL `RunState`: process-local even with Session Store; it does not survive worker loss.
|
|
66
|
+
- Recovery: replay the persisted application input against the same session.
|
|
67
|
+
|
|
68
|
+
The adapter sends every translated framework event through `context.emit()` before delivery. OpenAI
|
|
69
|
+
Agents SDK does not expose node-level checkpoint continuation, so side effects remain at-least-once
|
|
70
|
+
and tools must be idempotent.
|
|
71
|
+
|
|
72
|
+
## Tools
|
|
73
|
+
|
|
74
|
+
`agent/tools/all_tools()` auto-imports tool modules. Add a decorated tool file rather than manually
|
|
75
|
+
editing a registry. Approval tools must declare `needs_approval=True` and appear in
|
|
76
|
+
`REQUIRE_APPROVAL`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# OpenAI Agents template
|
|
2
|
+
|
|
3
|
+
An OpenAI Agents SDK agent served by `databricks_agentkit.DurableAgentServer`. The managed runtime keeps invocation state and
|
|
4
|
+
events in memory during `ab dev`. Deployment attaches a persistent Runtime Store, so invocation
|
|
5
|
+
state and events survive process loss and interrupted work can be recovered.
|
|
6
|
+
|
|
7
|
+
The generated project separates portable agent execution from the managed HTTP protocol:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
client -> runtime/main.py -> runtime/adapter.py -> agent/agent.py:run_agent
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- `agent/agent.py` owns the framework-native agent, sessions, tools, MCP lifetime, HITL state, and
|
|
14
|
+
`run_agent`.
|
|
15
|
+
- `runtime/adapter.py` owns the agent-author integration hooks: runtime input/output translation plus
|
|
16
|
+
`invoke` and `recover`.
|
|
17
|
+
- `runtime/main.py` constructs the server and registers those hooks.
|
|
18
|
+
|
|
19
|
+
To bring an existing Agents SDK agent, keep its normal execution code in `agent/agent.py`, expose a
|
|
20
|
+
`run_agent` function that returns the native streaming result, and make only the small payload/event
|
|
21
|
+
mapping changes needed in `runtime/adapter.py`.
|
|
22
|
+
|
|
23
|
+
## Run locally
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
ab dev
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The API is available at `http://localhost:8000/api/invocations`. Every request supplies a UUID `id`.
|
|
30
|
+
That ID is the invocation identifier and idempotency key. Agent-specific values live inside the
|
|
31
|
+
opaque `input` object:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
SESSION_ID=$(uuidgen)
|
|
35
|
+
INVOCATION_ID=$(uuidgen)
|
|
36
|
+
|
|
37
|
+
curl -sS http://localhost:8000/api/invocations \
|
|
38
|
+
-H 'Content-Type: application/json' \
|
|
39
|
+
-d "{\"id\":\"$INVOCATION_ID\",\"input\":{\"session_id\":\"$SESSION_ID\",\"messages\":[{\"role\":\"user\",\"content\":\"What time is it? Use your tool.\"}]}}"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Reuse `SESSION_ID` for multi-turn conversation history. Generate a new `INVOCATION_ID` for each
|
|
43
|
+
turn. Retrying the same request with the same invocation ID returns the persisted result; changing
|
|
44
|
+
the request while reusing the ID returns `409`.
|
|
45
|
+
|
|
46
|
+
## Invocation modes
|
|
47
|
+
|
|
48
|
+
- Foreground: omit `background` and `stream`; the response contains the agent result under `output`.
|
|
49
|
+
- Foreground streaming: set `stream: true`; the response is SSE backed by persisted events.
|
|
50
|
+
- Background: set `background: true`; poll the returned `status_url`.
|
|
51
|
+
- Background streaming: set both flags; the `202` response includes `status_url` and `events_url`.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
INVOCATION_ID=$(uuidgen)
|
|
55
|
+
curl -sN http://localhost:8000/api/invocations \
|
|
56
|
+
-H 'Content-Type: application/json' \
|
|
57
|
+
-d "{\"id\":\"$INVOCATION_ID\",\"input\":{\"session_id\":\"$SESSION_ID\",\"messages\":[{\"role\":\"user\",\"content\":\"Count to three.\"}]},\"stream\":true}"
|
|
58
|
+
|
|
59
|
+
INVOCATION_ID=$(uuidgen)
|
|
60
|
+
curl -sS http://localhost:8000/api/invocations \
|
|
61
|
+
-H 'Content-Type: application/json' \
|
|
62
|
+
-d "{\"id\":\"$INVOCATION_ID\",\"input\":{\"session_id\":\"$SESSION_ID\",\"messages\":[{\"role\":\"user\",\"content\":\"Summarize durable agents.\"}]},\"background\":true}" | jq
|
|
63
|
+
curl -sS "http://localhost:8000/api/invocations/$INVOCATION_ID" | jq
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
SSE records contain events translated by `runtime/adapter.py`: token `delta`s, completed `message`s,
|
|
67
|
+
and HITL `interrupt`s. Replay from a cursor with
|
|
68
|
+
`GET /api/invocations/{id}/events?after={sequence}`.
|
|
69
|
+
|
|
70
|
+
## Human approval
|
|
71
|
+
|
|
72
|
+
`send_message` requires approval. When output or the event stream contains an `interrupt`, submit a
|
|
73
|
+
new invocation with the same application session:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"id": "<new-uuid>",
|
|
78
|
+
"input": {
|
|
79
|
+
"session_id": "<same-session-id>",
|
|
80
|
+
"resume": {"decisions": [{"type": "approve"}]}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The paused Agents SDK `RunState` is process-local. A managed Session Store preserves transcript
|
|
86
|
+
history, but not a pending approval across restarts or replicas.
|
|
87
|
+
|
|
88
|
+
## Crash recovery
|
|
89
|
+
|
|
90
|
+
`runtime/main.py` always registers the adapter's `invoke` and `recover` hooks. Both call the same
|
|
91
|
+
`agent.agent.run_agent` function. OpenAI Agents SDK does not currently expose LangGraph-style node
|
|
92
|
+
checkpoints, so `recover` replays the original application input against the same session. The
|
|
93
|
+
adapter prepends a developer instruction telling the agent that this is a recovery attempt and that
|
|
94
|
+
some tool calls or external side effects may already have completed or may still be in progress.
|
|
95
|
+
When deployment attaches a Runtime Store, invocation state and emitted events survive process loss
|
|
96
|
+
and the runtime can call `recover` on a replacement worker. Without a Runtime Store, invocation state
|
|
97
|
+
remains process-local and interrupted work is not automatically recovered. External side effects
|
|
98
|
+
remain at-least-once and must be idempotent.
|
|
99
|
+
|
|
100
|
+
## Chat app
|
|
101
|
+
|
|
102
|
+
The browser UI is included by default. It generates a stable application session ID in local
|
|
103
|
+
storage, places it inside each invocation's `input`, and generates a fresh invocation UUID per turn.
|
|
104
|
+
Use `ab init --framework openai --disable-chat-app` for API-only output.
|
|
105
|
+
|
|
106
|
+
## Configure and deploy
|
|
107
|
+
|
|
108
|
+
- Change the model, instructions, tools, and framework-native execution in `agent/agent.py`.
|
|
109
|
+
- Change `runtime/adapter.py` only to map a different application input/output contract.
|
|
110
|
+
- Add local tools under `agent/tools/`; modules are auto-discovered.
|
|
111
|
+
- Add MCP servers in `agent/mcps.py` or with `ab tools add mcp`.
|
|
112
|
+
- Bind long-term memory with `ab memory bind <store>`.
|
|
113
|
+
- Bind durable transcript history with `ab sessions bind <store>`.
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
ab --profile <profile> deploy agent-openai --source .
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
When deployment provisions a dedicated Runtime Store, only the app-owned
|
|
120
|
+
`databricks_agentkit_runtime_<hash>` schema and runtime tables are added. Managed Runtime Store
|
|
121
|
+
deployments use their own default schema.
|
|
122
|
+
|
|
123
|
+
The `__Host-databricks-app-router` cookie may be supplied independently for sticky replica routing.
|
|
124
|
+
It is not authentication and is not used as the template's application session ID.
|
|
125
|
+
|
|
126
|
+
# Request-user authorization
|
|
127
|
+
|
|
128
|
+
Declared tools in `agent.toml` select `auth = "user"` or `auth = "app"`; legacy missing auth
|
|
129
|
+
continues to use the application/default identity. Deployed user tools require the request resolver
|
|
130
|
+
and never fall back to application credentials. Model, memory-service, session-service, and custom
|
|
131
|
+
MCP server credentials are unchanged.
|
|
132
|
+
|
|
133
|
+
`runtime/main.py` derives the invocation policy after `configure()`. The runtime adapter namespaces
|
|
134
|
+
public session IDs and actor values for the request owner, then passes only `workspace_client_for`
|
|
135
|
+
to the framework-native agent. Internal session keys are never returned to clients.
|
|
136
|
+
|
|
137
|
+
User-policy invocations use the existing Runtime for synchronous, streaming, and background calls,
|
|
138
|
+
including status polling, event replay, and invocation-ID idempotency. The Runtime Store persists no
|
|
139
|
+
credential; request-user authentication remains process-local for the active first attempt. A
|
|
140
|
+
replacement attempt fails with `MCP_USER_AUTH_RECOVERY_UNSUPPORTED` before agent code runs. Approval
|
|
141
|
+
interruptions remain unsupported and fail with `MCP_USER_AUTH_HITL_UNSUPPORTED`, without storing a
|
|
142
|
+
credential-bearing `RunState`. Existing namespaced memory and conversation-store behavior is
|
|
143
|
+
unchanged; OBO does not add another saver.
|
|
File without changes
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from collections.abc import AsyncIterator, Callable
|
|
5
|
+
from contextlib import asynccontextmanager, suppress
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from agents import Agent, Runner, RunResultStreaming, RunState
|
|
9
|
+
from agents.mcp import MCPServerManager
|
|
10
|
+
from databricks.sdk import WorkspaceClient
|
|
11
|
+
from databricks_openai import AsyncDatabricksOpenAI
|
|
12
|
+
|
|
13
|
+
from agent.mcps import build_mcp_servers
|
|
14
|
+
|
|
15
|
+
# Importing the tools package auto-registers every tool module.
|
|
16
|
+
from agent.tools import all_tools
|
|
17
|
+
from databricks_agentkit import workspace_client, workspace_headers
|
|
18
|
+
from databricks_agentkit.openai import (
|
|
19
|
+
configure_tracing,
|
|
20
|
+
genie_tools,
|
|
21
|
+
mcp_servers,
|
|
22
|
+
memory_tools,
|
|
23
|
+
session_store,
|
|
24
|
+
start_trace,
|
|
25
|
+
)
|
|
26
|
+
from databricks_agentkit.runtime.auth import AuthError
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
# A Unity Catalog AI Gateway model service, served from the `system.ai` schema and queried through
|
|
31
|
+
# the gateway (see `use_ai_gateway=True` in configure()). Swap for any `system.ai.*` model service
|
|
32
|
+
# your workspace exposes — the demo chat app's picker lists what's available.
|
|
33
|
+
MODEL = "system.ai.claude-sonnet-4-5"
|
|
34
|
+
|
|
35
|
+
# Tools that require human approval before they run. Add a tool's name here and the agent pauses when
|
|
36
|
+
# the model calls it, emitting an `interrupt` event; the client resumes by sending `resume` with the
|
|
37
|
+
# same session id. The tools declare `needs_approval=True` themselves (see agent/tools/); this set is
|
|
38
|
+
# how the runtime knows which pending calls to surface. Empty it to disable approval gating.
|
|
39
|
+
REQUIRE_APPROVAL = {"send_message"}
|
|
40
|
+
|
|
41
|
+
# OpenAI Sessions persist transcript history, not a paused RunState. Keep pending approvals local.
|
|
42
|
+
_pending_runs: dict[str, RunState] = {}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def configure() -> None:
|
|
46
|
+
"""Wire up global state; call once at server startup (not at import)."""
|
|
47
|
+
_check_databricks_auth()
|
|
48
|
+
from agents import set_default_openai_api, set_default_openai_client
|
|
49
|
+
|
|
50
|
+
# use_ai_gateway routes to the Unity Catalog AI Gateway (`<host>/ai-gateway/mlflow/v1`), so
|
|
51
|
+
# `MODEL` is a `system.ai.*` model name rather than a serving-endpoint name.
|
|
52
|
+
set_default_openai_client(
|
|
53
|
+
AsyncDatabricksOpenAI(
|
|
54
|
+
workspace_client=workspace_client(),
|
|
55
|
+
default_headers=workspace_headers() or None,
|
|
56
|
+
use_ai_gateway=True,
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
set_default_openai_api("chat_completions")
|
|
60
|
+
configure_tracing()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _check_databricks_auth() -> None:
|
|
64
|
+
"""Fail fast at startup with a clear message if Databricks auth isn't configured.
|
|
65
|
+
|
|
66
|
+
Without this, a missing/invalid profile only surfaces on the first model call — as a generic SDK
|
|
67
|
+
error buried in a request traceback. Resolving a WorkspaceClient here validates the same config
|
|
68
|
+
the model client uses, so the failure is immediate and actionable.
|
|
69
|
+
"""
|
|
70
|
+
try:
|
|
71
|
+
workspace_client()
|
|
72
|
+
except Exception as e:
|
|
73
|
+
profile = os.getenv("DATABRICKS_CONFIG_PROFILE")
|
|
74
|
+
target = (
|
|
75
|
+
f"profile {profile!r}" if profile else "the DEFAULT profile / DATABRICKS_HOST+TOKEN"
|
|
76
|
+
)
|
|
77
|
+
raise RuntimeError(
|
|
78
|
+
f"Databricks auth is not configured — the agent can't call the model. Tried {target}.\n"
|
|
79
|
+
"Fix one of:\n"
|
|
80
|
+
" • set DATABRICKS_CONFIG_PROFILE in .env to a profile from `databricks auth profiles`, or\n"
|
|
81
|
+
" • run `databricks auth login --profile <name>` to create one, or\n"
|
|
82
|
+
" • set DATABRICKS_HOST and DATABRICKS_TOKEN in .env.\n"
|
|
83
|
+
f"(underlying error: {e})"
|
|
84
|
+
) from e
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def create_agent(
|
|
88
|
+
actor: str,
|
|
89
|
+
mcp=None,
|
|
90
|
+
model: str | None = None,
|
|
91
|
+
*,
|
|
92
|
+
workspace_client_for: Callable[[str], WorkspaceClient] | None = None,
|
|
93
|
+
) -> Agent:
|
|
94
|
+
"""Build the OpenAI Agents SDK agent: tools, memory, MCP servers, and model."""
|
|
95
|
+
return Agent(
|
|
96
|
+
name="Agent",
|
|
97
|
+
instructions="You are a helpful assistant.",
|
|
98
|
+
model=model or MODEL,
|
|
99
|
+
tools=[
|
|
100
|
+
*all_tools(),
|
|
101
|
+
*memory_tools(actor),
|
|
102
|
+
*genie_tools(workspace_client_for=workspace_client_for),
|
|
103
|
+
],
|
|
104
|
+
mcp_servers=mcp or [],
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def resume_agent(session_id: str, resume: dict[str, Any]) -> RunState:
|
|
109
|
+
"""Apply human decisions to a paused run and return the native RunState."""
|
|
110
|
+
state = _pending_runs.pop(session_id, None)
|
|
111
|
+
if state is None:
|
|
112
|
+
raise RuntimeError(
|
|
113
|
+
"No paused run for this session. HITL pauses are in-process only, so a restart or a "
|
|
114
|
+
"different replica loses them; retry the turn."
|
|
115
|
+
)
|
|
116
|
+
decisions = resume.get("decisions") or []
|
|
117
|
+
for decision, item in zip(decisions, state.get_interruptions(), strict=False):
|
|
118
|
+
if decision.get("type") == "approve":
|
|
119
|
+
state.approve(item)
|
|
120
|
+
else:
|
|
121
|
+
state.reject(item, rejection_message=decision.get("message"))
|
|
122
|
+
return state
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@asynccontextmanager
|
|
126
|
+
async def run_agent(
|
|
127
|
+
agent_input: list[Any] | RunState,
|
|
128
|
+
*,
|
|
129
|
+
session_id: str,
|
|
130
|
+
actor: str | None = None,
|
|
131
|
+
model: str | None = None,
|
|
132
|
+
workspace_client_for: Callable[[str], WorkspaceClient] | None = None,
|
|
133
|
+
) -> AsyncIterator[RunResultStreaming]:
|
|
134
|
+
"""Run the agent and expose its native streaming result.
|
|
135
|
+
|
|
136
|
+
This is the framework-native entrypoint. It has no dependency on Agent Bricks request or context types,
|
|
137
|
+
so it can be called from another server, a notebook, or a test harness.
|
|
138
|
+
"""
|
|
139
|
+
actor = actor or session_id
|
|
140
|
+
auth_kwargs = {"workspace_client_for": workspace_client_for} if workspace_client_for else {}
|
|
141
|
+
servers = await mcp_servers(build_mcp_servers(), **auth_kwargs)
|
|
142
|
+
async with MCPServerManager(servers) as manager:
|
|
143
|
+
for server, error in manager.errors.items():
|
|
144
|
+
if getattr(server, "_agentbricks_request_user", False) is True or isinstance(
|
|
145
|
+
error, AuthError
|
|
146
|
+
):
|
|
147
|
+
raise error
|
|
148
|
+
active_servers = []
|
|
149
|
+
for server in manager.active_servers:
|
|
150
|
+
tool_filter = server.tool_filter
|
|
151
|
+
try:
|
|
152
|
+
server.tool_filter = None
|
|
153
|
+
server.cache_tools_list = True
|
|
154
|
+
async with asyncio.timeout(manager.connect_timeout_seconds):
|
|
155
|
+
await server.list_tools()
|
|
156
|
+
except Exception as error:
|
|
157
|
+
if getattr(server, "_agentbricks_request_user", False) is True or isinstance(
|
|
158
|
+
error, AuthError
|
|
159
|
+
):
|
|
160
|
+
raise
|
|
161
|
+
logger.warning(
|
|
162
|
+
"Failed to list tools from MCP server %r; continuing without it.",
|
|
163
|
+
server.name,
|
|
164
|
+
exc_info=True,
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
active_servers.append(server)
|
|
168
|
+
finally:
|
|
169
|
+
server.tool_filter = tool_filter
|
|
170
|
+
|
|
171
|
+
agent = create_agent(
|
|
172
|
+
actor,
|
|
173
|
+
active_servers,
|
|
174
|
+
model=model,
|
|
175
|
+
workspace_client_for=workspace_client_for,
|
|
176
|
+
)
|
|
177
|
+
with start_trace(name="invoke", inputs=agent_input, session_id=session_id) as span:
|
|
178
|
+
if isinstance(agent_input, RunState):
|
|
179
|
+
result = Runner.run_streamed(agent, agent_input)
|
|
180
|
+
else:
|
|
181
|
+
result = Runner.run_streamed(
|
|
182
|
+
agent,
|
|
183
|
+
agent_input,
|
|
184
|
+
session=session_store(session_id, actor),
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
yield result
|
|
189
|
+
finally:
|
|
190
|
+
if workspace_client_for is not None and not result.is_complete:
|
|
191
|
+
result.cancel()
|
|
192
|
+
with suppress(asyncio.CancelledError, Exception):
|
|
193
|
+
async for _ in result.stream_events():
|
|
194
|
+
pass
|
|
195
|
+
|
|
196
|
+
if result.interruptions:
|
|
197
|
+
if workspace_client_for is not None:
|
|
198
|
+
raise AuthError(
|
|
199
|
+
"MCP_USER_AUTH_HITL_UNSUPPORTED",
|
|
200
|
+
"Request-user invocations do not support paused approvals.",
|
|
201
|
+
400,
|
|
202
|
+
)
|
|
203
|
+
_pending_runs[session_id] = result.to_state()
|
|
204
|
+
if span is not None:
|
|
205
|
+
span.set_outputs({"output": result.final_output})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""MCP servers to offer the agent — this is where you configure them.
|
|
2
|
+
|
|
3
|
+
Empty by default: the agent runs with only the MCP servers declared in ``agent.toml``. Add servers
|
|
4
|
+
to ``build_mcp_servers`` to offer more; ``agent.py`` joins them with the ``agent.toml`` servers and
|
|
5
|
+
omits any that fail to connect or list tools.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from databricks_openai.agents import McpServer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def build_mcp_servers() -> list[McpServer]:
|
|
12
|
+
"""Return the MCP servers to offer the agent. Empty by default — add your own.
|
|
13
|
+
|
|
14
|
+
Example (a Databricks-managed MCP, authed as the app service principal)::
|
|
15
|
+
|
|
16
|
+
from databricks.sdk import WorkspaceClient
|
|
17
|
+
|
|
18
|
+
client = WorkspaceClient()
|
|
19
|
+
host = client.config.host.rstrip("/")
|
|
20
|
+
return [
|
|
21
|
+
McpServer(
|
|
22
|
+
url=f"{host}/api/2.0/mcp/functions/system/ai",
|
|
23
|
+
name="system-ai",
|
|
24
|
+
workspace_client=client,
|
|
25
|
+
),
|
|
26
|
+
]
|
|
27
|
+
"""
|
|
28
|
+
return []
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Agent tools package.
|
|
2
|
+
|
|
3
|
+
Every module here is auto-imported, and every OpenAI Agents SDK ``FunctionTool`` it defines (via the
|
|
4
|
+
``@function_tool`` decorator) is collected by ``all_tools()``. Drop a new ``*.py`` into this folder
|
|
5
|
+
with a ``@function_tool``-decorated function and it's picked up automatically — no wiring to edit.
|
|
6
|
+
``create_agent`` uses ``all_tools()``.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import importlib
|
|
10
|
+
import inspect
|
|
11
|
+
import pkgutil
|
|
12
|
+
|
|
13
|
+
from agents import FunctionTool
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def all_tools() -> list[FunctionTool]:
|
|
17
|
+
"""Every FunctionTool defined across the modules in this package."""
|
|
18
|
+
tools: list[FunctionTool] = []
|
|
19
|
+
for module in pkgutil.iter_modules(__path__):
|
|
20
|
+
mod = importlib.import_module(f"{__name__}.{module.name}")
|
|
21
|
+
for _, obj in inspect.getmembers(mod, lambda o: isinstance(o, FunctionTool)):
|
|
22
|
+
if obj not in tools: # a tool imported into several modules is collected once
|
|
23
|
+
tools.append(obj)
|
|
24
|
+
return tools
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Sample tool. A working example — add your own tools as new files in this package.
|
|
2
|
+
|
|
3
|
+
Decorate a function with ``@function_tool`` (OpenAI Agents SDK) and it becomes an agent tool; the
|
|
4
|
+
package auto-collects it via ``all_tools()``, which ``create_agent`` uses.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
from agents import function_tool
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@function_tool
|
|
13
|
+
def get_current_time() -> str:
|
|
14
|
+
"""Get the current date and time."""
|
|
15
|
+
return datetime.now().isoformat()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""A side-effecting sample tool, gated by human approval.
|
|
2
|
+
|
|
3
|
+
Unlike ``get_current_time`` (a harmless read), this tool stands in for an action with real
|
|
4
|
+
consequences — sending a message. ``needs_approval=True`` makes the Agents SDK pause before it runs
|
|
5
|
+
and surface a pending approval; the runtime relays that as an ``interrupt`` event and resumes on an
|
|
6
|
+
approve/reject decision (its name is also listed in ``REQUIRE_APPROVAL`` in ``agent/agent.py``). Swap
|
|
7
|
+
the body for a real send; the approval gate is what the template is demonstrating.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from agents import function_tool
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@function_tool(needs_approval=True)
|
|
14
|
+
def send_message(recipient: str, body: str) -> str:
|
|
15
|
+
"""Send a message to a recipient. Use when the user asks to notify or message someone."""
|
|
16
|
+
# A real implementation would call an email/Slack/SMS API here. The point of this template is the
|
|
17
|
+
# human-approval gate in front of it (needs_approval=True), not the delivery.
|
|
18
|
+
return f"Message sent to {recipient}: {body}"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
command: ["uv", "run", "start-server"]
|
|
2
|
+
# databricks apps listen by default on port 8000
|
|
3
|
+
#
|
|
4
|
+
# Agent Bricks injects the deployed Runtime Store endpoint. The Agents SDK session remains
|
|
5
|
+
# in-process unless a managed Session Store is bound. Tracing is bound with `ab tracing bind
|
|
6
|
+
# --experiment-name <path>` (ab deploy then wires the env below; `ab dev` traces to a local
|
|
7
|
+
# MLflow server). To set a deployment's tracing env by hand instead:
|
|
8
|
+
#
|
|
9
|
+
# env:
|
|
10
|
+
# # Tracing (set a destination + an experiment). Destination: MLFLOW_TRACKING_URI or
|
|
11
|
+
# # MLFLOW_TRACING_DESTINATION. Experiment: MLFLOW_EXPERIMENT_ID or MLFLOW_EXPERIMENT_NAME.
|
|
12
|
+
# - name: MLFLOW_TRACKING_URI
|
|
13
|
+
# value: "databricks"
|
|
14
|
+
# - name: MLFLOW_EXPERIMENT_ID
|
|
15
|
+
# value: "<experiment-id>"
|