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,27 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from databricks_langchain import ChatDatabricks
|
|
4
|
+
from langchain.agents import create_agent
|
|
5
|
+
|
|
6
|
+
from databricks_agentkit import workspace_client, workspace_headers
|
|
7
|
+
|
|
8
|
+
MODEL = "databricks-gpt-5-2"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class _RoutedChatDatabricks(ChatDatabricks):
|
|
12
|
+
def _get_client_kwargs(self) -> dict[str, Any]:
|
|
13
|
+
kwargs = super()._get_client_kwargs()
|
|
14
|
+
if headers := workspace_headers():
|
|
15
|
+
kwargs["default_headers"] = headers
|
|
16
|
+
return kwargs
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def invoke(messages: list[dict[str, Any]]) -> dict[str, Any]:
|
|
20
|
+
agent = create_agent(
|
|
21
|
+
model=_RoutedChatDatabricks(endpoint=MODEL, workspace_client=workspace_client()),
|
|
22
|
+
tools=[],
|
|
23
|
+
)
|
|
24
|
+
result = await agent.ainvoke({"messages": messages})
|
|
25
|
+
message = result["messages"][-1]
|
|
26
|
+
output = message.model_dump() if hasattr(message, "model_dump") else message
|
|
27
|
+
return {"output": [output]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
command: ["uv", "run", "start-server"]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "custom-agent-langgraph"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "LangGraph agent with a custom FastAPI server"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"databricks-agentbricks>=0.2.0",
|
|
9
|
+
"databricks-langchain>=0.17.0",
|
|
10
|
+
"databricks-sdk>=0.124.0",
|
|
11
|
+
"fastapi>=0.129.0",
|
|
12
|
+
"langchain>=1.0.0",
|
|
13
|
+
"python-dotenv>=1.2.1",
|
|
14
|
+
"uvicorn>=0.41.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["hatchling"]
|
|
19
|
+
build-backend = "hatchling.build"
|
|
20
|
+
|
|
21
|
+
[tool.hatch.build.targets.wheel]
|
|
22
|
+
packages = ["agent", "runtime"]
|
|
23
|
+
|
|
24
|
+
[dependency-groups]
|
|
25
|
+
dev = [
|
|
26
|
+
"hatchling>=1.28.0",
|
|
27
|
+
"httpx>=0.28.1",
|
|
28
|
+
"pytest>=9.0.2",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[tool.uv]
|
|
32
|
+
default-groups = ["dev"]
|
|
33
|
+
|
|
34
|
+
[tool.pytest.ini_options]
|
|
35
|
+
testpaths = ["tests"]
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
start-server = "runtime.main:main"
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import uvicorn
|
|
6
|
+
from agent.agent import invoke
|
|
7
|
+
from dotenv import load_dotenv
|
|
8
|
+
from fastapi import FastAPI
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
10
|
+
|
|
11
|
+
# override=False so injected DATABRICKS_* (from `ab dev -p` or the deploy platform) win over a
|
|
12
|
+
# checked-in .env.
|
|
13
|
+
load_dotenv(dotenv_path=Path(__file__).parent.parent / ".env", override=False)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class InvocationRequest(BaseModel):
|
|
17
|
+
model_config = ConfigDict(extra="forbid")
|
|
18
|
+
|
|
19
|
+
input: list[dict[str, Any]] = Field(default_factory=list)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
app = FastAPI(title="Custom LangGraph Agent")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@app.post("/invocations")
|
|
26
|
+
async def invocations(body: InvocationRequest) -> dict[str, Any]:
|
|
27
|
+
return await invoke(body.input)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def main() -> None:
|
|
31
|
+
uvicorn.run("runtime.main:app", host="0.0.0.0", port=int(os.getenv("PORT", "8000")))
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from unittest.mock import AsyncMock
|
|
2
|
+
|
|
3
|
+
from fastapi.testclient import TestClient
|
|
4
|
+
|
|
5
|
+
import runtime.main as runtime
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_foreground_invocation(monkeypatch) -> None:
|
|
9
|
+
invoke = AsyncMock(return_value={"output": [{"role": "assistant", "content": "hello"}]})
|
|
10
|
+
monkeypatch.setattr(runtime, "invoke", invoke)
|
|
11
|
+
|
|
12
|
+
response = TestClient(runtime.app).post(
|
|
13
|
+
"/invocations",
|
|
14
|
+
json={"input": [{"role": "user", "content": "hi"}]},
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
assert response.status_code == 200
|
|
18
|
+
assert response.json()["output"][0]["content"] == "hello"
|
|
19
|
+
invoke.assert_awaited_once_with([{"role": "user", "content": "hi"}])
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_custom_server_rejects_runtime_transport_fields() -> None:
|
|
23
|
+
response = TestClient(runtime.app).post(
|
|
24
|
+
"/invocations",
|
|
25
|
+
json={"input": [], "background": True},
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
assert response.status_code == 422
|
|
29
|
+
assert "/api/invocations" not in runtime.app.openapi()["paths"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DATABRICKS_CONFIG_PROFILE=DEFAULT
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Custom OpenAI Agents server
|
|
2
|
+
|
|
3
|
+
This template shows how to serve an OpenAI Agents SDK agent with an ordinary FastAPI application.
|
|
4
|
+
It does not use the managed `DurableAgentServer` HTTP server or durable runtime.
|
|
5
|
+
|
|
6
|
+
This template does not load managed tool bindings from `agent.toml`, so `ab tools add` is not
|
|
7
|
+
supported. Wire framework-native Python tools and MCP servers directly in `agent/agent.py`.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
ab dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Call its single foreground endpoint:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
curl -sS http://localhost:8000/invocations \
|
|
17
|
+
-H 'Content-Type: application/json' \
|
|
18
|
+
-d '{"input":[{"role":"user","content":"Hello"}]}'
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Edit `runtime/main.py` to define your own HTTP contract. Edit `agent/agent.py` to change the model
|
|
22
|
+
or agent behavior. Deploy with `ab --profile <profile> deploy custom-agent-openai`.
|
|
File without changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from agents import Agent, Runner, set_default_openai_api, set_default_openai_client
|
|
4
|
+
from databricks_openai import AsyncDatabricksOpenAI
|
|
5
|
+
|
|
6
|
+
from databricks_agentkit import workspace_client, workspace_headers
|
|
7
|
+
|
|
8
|
+
MODEL = "databricks-gpt-5-2"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _configure_client() -> None:
|
|
12
|
+
headers = workspace_headers()
|
|
13
|
+
workspace = workspace_client()
|
|
14
|
+
client = (
|
|
15
|
+
AsyncDatabricksOpenAI(workspace_client=workspace, default_headers=headers)
|
|
16
|
+
if headers
|
|
17
|
+
else AsyncDatabricksOpenAI(workspace_client=workspace)
|
|
18
|
+
)
|
|
19
|
+
set_default_openai_client(client)
|
|
20
|
+
set_default_openai_api("chat_completions")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def invoke(messages: list[dict[str, Any]]) -> dict[str, Any]:
|
|
24
|
+
_configure_client()
|
|
25
|
+
result = await Runner.run(
|
|
26
|
+
Agent(name="Agent", instructions="You are a helpful assistant.", model=MODEL),
|
|
27
|
+
messages,
|
|
28
|
+
)
|
|
29
|
+
return {"output": [{"role": "assistant", "content": str(result.final_output)}]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
command: ["uv", "run", "start-server"]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "custom-agent-openai"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "OpenAI Agents SDK agent with a custom FastAPI server"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"databricks-agentbricks>=0.2.0",
|
|
9
|
+
"databricks-openai>=0.13.0",
|
|
10
|
+
"databricks-sdk>=0.124.0",
|
|
11
|
+
"fastapi>=0.129.0",
|
|
12
|
+
"openai-agents>=0.8.0",
|
|
13
|
+
"python-dotenv>=1.2.1",
|
|
14
|
+
"uvicorn>=0.41.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["hatchling"]
|
|
19
|
+
build-backend = "hatchling.build"
|
|
20
|
+
|
|
21
|
+
[tool.hatch.build.targets.wheel]
|
|
22
|
+
packages = ["agent", "runtime"]
|
|
23
|
+
|
|
24
|
+
[dependency-groups]
|
|
25
|
+
dev = [
|
|
26
|
+
"hatchling>=1.28.0",
|
|
27
|
+
"httpx>=0.28.1",
|
|
28
|
+
"pytest>=9.0.2",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[tool.uv]
|
|
32
|
+
default-groups = ["dev"]
|
|
33
|
+
|
|
34
|
+
[tool.pytest.ini_options]
|
|
35
|
+
testpaths = ["tests"]
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
start-server = "runtime.main:main"
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import uvicorn
|
|
6
|
+
from agent.agent import invoke
|
|
7
|
+
from dotenv import load_dotenv
|
|
8
|
+
from fastapi import FastAPI
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
10
|
+
|
|
11
|
+
# override=False so injected DATABRICKS_* (from `ab dev -p` or the deploy platform) win over a
|
|
12
|
+
# checked-in .env.
|
|
13
|
+
load_dotenv(dotenv_path=Path(__file__).parent.parent / ".env", override=False)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class InvocationRequest(BaseModel):
|
|
17
|
+
model_config = ConfigDict(extra="forbid")
|
|
18
|
+
|
|
19
|
+
input: list[dict[str, Any]] = Field(default_factory=list)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
app = FastAPI(title="Custom OpenAI Agent")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@app.post("/invocations")
|
|
26
|
+
async def invocations(body: InvocationRequest) -> dict[str, Any]:
|
|
27
|
+
return await invoke(body.input)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def main() -> None:
|
|
31
|
+
uvicorn.run("runtime.main:app", host="0.0.0.0", port=int(os.getenv("PORT", "8000")))
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from unittest.mock import AsyncMock
|
|
2
|
+
|
|
3
|
+
from fastapi.testclient import TestClient
|
|
4
|
+
|
|
5
|
+
import runtime.main as runtime
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_foreground_invocation(monkeypatch) -> None:
|
|
9
|
+
invoke = AsyncMock(return_value={"output": [{"role": "assistant", "content": "hello"}]})
|
|
10
|
+
monkeypatch.setattr(runtime, "invoke", invoke)
|
|
11
|
+
|
|
12
|
+
response = TestClient(runtime.app).post(
|
|
13
|
+
"/invocations",
|
|
14
|
+
json={"input": [{"role": "user", "content": "hi"}]},
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
assert response.status_code == 200
|
|
18
|
+
assert response.json()["output"][0]["content"] == "hello"
|
|
19
|
+
invoke.assert_awaited_once_with([{"role": "user", "content": "hi"}])
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_custom_server_rejects_runtime_transport_fields() -> None:
|
|
23
|
+
response = TestClient(runtime.app).post(
|
|
24
|
+
"/invocations",
|
|
25
|
+
json={"input": [], "stream": True},
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
assert response.status_code == 422
|
|
29
|
+
assert "/api/invocations" not in runtime.app.openapi()["paths"]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from databricks.sdk import WorkspaceClient
|
|
4
|
+
from databricks_openai.agents import McpServer # ty: ignore[unresolved-import]
|
|
5
|
+
|
|
6
|
+
# BEGIN: agentbricks add-sandbox
|
|
7
|
+
# The downscope is fixed when this file is generated. It is sent as MCP metadata,
|
|
8
|
+
# outside model-controlled tool arguments, on every sandbox call.
|
|
9
|
+
_SANDBOX_DOWNSCOPE: dict[str, list[dict[str, str]]] = {} # __AGENTBRICKS_SANDBOX_DOWNSCOPE__
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class _SandboxMcpServer(McpServer):
|
|
13
|
+
async def call_tool(
|
|
14
|
+
self,
|
|
15
|
+
tool_name: str,
|
|
16
|
+
arguments: dict[str, Any] | None,
|
|
17
|
+
**kwargs: Any,
|
|
18
|
+
) -> Any:
|
|
19
|
+
incoming_meta = kwargs.pop("meta", None)
|
|
20
|
+
meta = dict(incoming_meta) if isinstance(incoming_meta, dict) else {}
|
|
21
|
+
meta["downscope"] = _SANDBOX_DOWNSCOPE
|
|
22
|
+
return await super().call_tool(tool_name, arguments, meta=meta, **kwargs)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _build_sandbox_mcp_server() -> McpServer:
|
|
26
|
+
workspace_client = WorkspaceClient()
|
|
27
|
+
return _SandboxMcpServer(
|
|
28
|
+
url=(
|
|
29
|
+
f"{workspace_client.config.host.rstrip('/')}/ai-gateway/mcp-services/system.ai.sandbox"
|
|
30
|
+
),
|
|
31
|
+
workspace_client=workspace_client,
|
|
32
|
+
timeout=120.0,
|
|
33
|
+
name="system.ai.sandbox",
|
|
34
|
+
# Production advertises ``sandbox``; older preview workspaces use ``run_code``.
|
|
35
|
+
tool_filter={"allowed_tool_names": ["sandbox", "run_code"]},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# END: agentbricks add-sandbox
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from databricks.sdk import WorkspaceClient
|
|
4
|
+
from databricks_langchain import DatabricksMCPServer # ty: ignore[unresolved-import]
|
|
5
|
+
from langchain_mcp_adapters.sessions import create_session # ty: ignore[unresolved-import]
|
|
6
|
+
|
|
7
|
+
# BEGIN: agentbricks add-sandbox
|
|
8
|
+
# The downscope is fixed when this file is generated. It is sent as MCP metadata,
|
|
9
|
+
# outside model-controlled tool arguments, on every sandbox call.
|
|
10
|
+
_SANDBOX_DOWNSCOPE: dict[str, list[dict[str, str]]] = {} # __AGENTBRICKS_SANDBOX_DOWNSCOPE__
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _build_sandbox_mcp_server() -> DatabricksMCPServer:
|
|
14
|
+
workspace_client = WorkspaceClient()
|
|
15
|
+
return DatabricksMCPServer(
|
|
16
|
+
name="system.ai.sandbox",
|
|
17
|
+
url=(
|
|
18
|
+
f"{workspace_client.config.host.rstrip('/')}/ai-gateway/mcp-services/system.ai.sandbox"
|
|
19
|
+
),
|
|
20
|
+
workspace_client=workspace_client,
|
|
21
|
+
timeout=120.0,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def _sandbox_tool_interceptor(request: Any, handler: Any) -> Any:
|
|
26
|
+
if request.server_name != "system.ai.sandbox":
|
|
27
|
+
return await handler(request)
|
|
28
|
+
|
|
29
|
+
connection = _build_sandbox_mcp_server().to_connection_dict()
|
|
30
|
+
async with create_session(connection) as session:
|
|
31
|
+
await session.initialize()
|
|
32
|
+
return await session.call_tool(
|
|
33
|
+
request.name,
|
|
34
|
+
request.args,
|
|
35
|
+
meta={"downscope": _SANDBOX_DOWNSCOPE},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# END: agentbricks add-sandbox
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# LangGraph Chat App Overlay
|
|
2
|
+
|
|
3
|
+
`ab init --framework langgraph` copies this framework-specific overlay after the base
|
|
4
|
+
`agent-langgraph` template (it is included by default; `--disable-chat-app` opts out). It is
|
|
5
|
+
intentionally not a post-generation mutation command.
|
|
6
|
+
|
|
7
|
+
## Installed files
|
|
8
|
+
|
|
9
|
+
- `ui/` contains the zero-build chat client.
|
|
10
|
+
- `runtime/ui.py` serves the assets and exposes demo APIs for memory and sessions.
|
|
11
|
+
- `runtime/main.py` installs the chat routes on the base FastAPI runtime.
|
|
12
|
+
- `tests/test_demo_ui.py` verifies the browser-facing routes.
|
|
13
|
+
|
|
14
|
+
## Behavior
|
|
15
|
+
|
|
16
|
+
The capability indicators are automatic. Streaming and background reflect the runtime contract;
|
|
17
|
+
Session reflects checkpoint history; Memory requires `AGENT_MEMORY_STORE`; Traces reflects the MLflow
|
|
18
|
+
tracing config (destination + experiment). Selecting Memory opens a pane for browsing the store's
|
|
19
|
+
entries (searchable, filterable by actor, with a modal for each entry); the other indicators expand
|
|
20
|
+
in place, and Traces links out to the MLflow experiment. The transport selector is the only manual
|
|
21
|
+
capability choice.
|
|
22
|
+
|
|
23
|
+
The composer's model picker lists the workspace's Unity Catalog AI Gateway chat model services — the
|
|
24
|
+
`system.ai.*` model services from `GET /api/2.1/unity-catalog/model-services?parent=schemas/system.ai`
|
|
25
|
+
(embeddings-only services filtered out), exposed as `GET /api/demo/models` and pinned to
|
|
26
|
+
`agent.agent.MODEL` as the default. Each request sends the selected model as `model` in the
|
|
27
|
+
invocation body; the agent is rebuilt per turn, so the picker changes the model for the next turn
|
|
28
|
+
without a restart. Discovery is best-effort: if listing is unavailable (e.g. `system.ai` isn't
|
|
29
|
+
readable), the picker falls back to just the default. Omitting `model` uses `MODEL`.
|
|
30
|
+
|
|
31
|
+
The agent calls the chosen model through the gateway (`<host>/ai-gateway/mlflow/v1`) rather than
|
|
32
|
+
`/serving-endpoints`, so `MODEL` is a `system.ai.*` model service name. The picker is capped
|
|
33
|
+
(`_MODEL_LIMIT`, 20) with the default pinned first and the rest alphabetical, so truncation never
|
|
34
|
+
drops the configured default. Transient list failures are retried in
|
|
35
|
+
`databricks_agentkit.runtime.model_services` before the fallback applies.
|
|
36
|
+
|
|
37
|
+
The UI reads local history from the LangGraph checkpoint and managed history from Session Store
|
|
38
|
+
items. It keeps a stable application session UUID in browser local storage and includes it inside
|
|
39
|
+
every durable invocation's `input`; each turn gets a separate invocation UUID. The router cookie is
|
|
40
|
+
independent and may still provide sticky replica routing.
|
|
41
|
+
|
|
42
|
+
The Sessions card creates new session UUIDs in the browser. With a managed Session Store,
|
|
43
|
+
`GET /api/demo/sessions` lists the most recent sessions for the signed-in actor and each Open action
|
|
44
|
+
calls `POST /api/demo/sessions/{session_id}/open`. Opening a session verifies ownership and reloads
|
|
45
|
+
its transcript and pending LangGraph state. In local in-memory mode only the current browser session
|
|
46
|
+
can be listed because there is no shared session index.
|
|
47
|
+
|
|
48
|
+
Transcript responses include only user, assistant, tool, system, and human-decision message items;
|
|
49
|
+
checkpoint fragments remain in Session Store but are never returned to the chat UI.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Run the LangGraph agent with the optional Agent Bricks chat app."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import uvicorn
|
|
7
|
+
from agent.agent import configure
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
from runtime.adapter import invoke, recover
|
|
10
|
+
from runtime.ui import install_ui
|
|
11
|
+
|
|
12
|
+
from databricks_agentkit import DurableAgentServer
|
|
13
|
+
|
|
14
|
+
# override=False so injected DATABRICKS_* (from `ab dev -p` or the deploy platform) win over a
|
|
15
|
+
# checked-in .env.
|
|
16
|
+
load_dotenv(dotenv_path=Path(__file__).parent.parent / ".env", override=False)
|
|
17
|
+
configure()
|
|
18
|
+
|
|
19
|
+
app = DurableAgentServer()
|
|
20
|
+
app.invoke(invoke)
|
|
21
|
+
app.recover(recover)
|
|
22
|
+
install_ui(app)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def main() -> None:
|
|
26
|
+
uvicorn.run("runtime.main:app", host="0.0.0.0", port=int(os.getenv("PORT", "8000")))
|