Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 118 additions & 16 deletions gcloud/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,121 @@
# Google Cloud deployment modes
# Google Cloud deployment modes

Select AI supports two distinct Google Cloud deployment modes. Choose based on
whether the database and Select AI team are known at deployment time or must
be selected dynamically by each user.
Select AI for Python supports two A2A deployment architectures. The key
decisions are where the database connection and Select AI team are selected,
which components carry the session, and how capacity is added:

| | Standalone | Dynamic gateway |
- Standalone fixes the database and team at deployment time. One Cloud Run A2A
service owns the configured connection pool and serves that team.
- The gateway selects the database and team per user session. A Cloud Run A2A
gateway routes sessions through Consul to a clustered GKE worker pool, with
one isolated child runtime and database connection pool per active session.

The gateway architecture is designed for horizontal session capacity. Gateway
instances, Consul, and worker replicas are separate components; adding worker
replicas increases the number of concurrent database sessions that can be
hosted behind the same A2A endpoint. Consul preserves session and task affinity
when requests reach different gateway instances. Oracle Database capacity and
the configured session TTL remain the limiting factors.

## What the A2A client connects to

### Standalone server

The standalone deployment is one Cloud Run A2A service for one configured
Oracle database and one Select AI team.

```text
A2A client ── A2A JSON-RPC ──► Cloud Run A2A server ──► Oracle Database
fixed credentials
fixed team
```

The service receives its database credentials from Secret Manager. The A2A
client can discover the Agent Card and immediately send a database prompt.
The server supports blocking tasks, task polling, and streaming responses.

Deploy it with:

```bash
gcloud/standalone/deploy.sh --build
```

Use [standalone deployment](standalone/README.md) for the deployment details.

### Dynamic gateway

The gateway deployment provides one public A2A endpoint for users who choose
the database and Select AI team at runtime.

```text
A2A client ── A2A JSON-RPC ──► Cloud Run gateway
│ A2UI connection form
GKE worker session ──► Oracle Database
│ Consul session/task routing
```

The client first sends a message and receives an A2UI connection form. After
the client submits the DSN, username, password, and team name, the gateway
opens a temporary worker session. Subsequent A2A messages use that session and
execute against the selected database and team.

The gateway supports blocking tasks and asynchronous task polling. Its Agent
Card advertises `streaming: false`; clients use `message/send` followed by
`tasks/get` for long-running work. The gateway-to-worker path uses internal
protobuf messages, while the public client-facing path remains A2A JSON-RPC.

The gateway database session currently accepts a DSN, username, and password.
Wallet-based Oracle Database mTLS is not yet supported by this session path.
The optional mTLS deployment mode described in the gateway documentation
secures the gateway-to-worker connection; it is separate from database mTLS.

Deploy it with:

```bash
gcloud/gateway/deploy.sh --project PROJECT_ID
```

Use [gateway deployment](gateway/README.md) for the deployment details.

## Client-visible differences

| Client concern | Standalone server | Dynamic gateway |
| --- | --- | --- |
| Database and team | Fixed at deployment time | Chosen at runtime for each user session |
| Public A2A service | One service for one configured team | One gateway that presents an A2UI connection form |
| Users | All requests use the deployed database identity | Any permitted user can connect to a reachable Oracle database and Select AI team |
| Architecture | One Cloud Run service | Cloud Run gateway, plus Consul and worker replicas in GKE |
| Session isolation | Shared service database pool | One child process and async pool per active user session |
| Main benefit | Simple, predictable deployment | Dynamic, multi-database and multi-team access from one A2A endpoint |
| Operational cost | Low | Higher: GKE workers, Consul, routing, TTL, and session capacity |

Use [standalone](standalone/README.md) when a service should expose one known
database team. Use [gateway](gateway/README.md) when users must dynamically
choose their database connection and team.
| Database/team selection | Configured by the deployment | Submitted by each user session through A2UI |
| First client operation | Send the database prompt | Send a prompt, submit the connection form, then send the database prompt |
| Credentials | Stored in Secret Manager for the service | Supplied for the temporary session and held by its worker |
| Database mTLS | Supported through the standalone wallet configuration | Not yet supported for gateway database sessions |
| Public service | One Cloud Run A2A service | Cloud Run gateway backed by GKE workers and Consul |
| Agent Card input | `text/plain` | `text/plain` and `application/json+a2ui` |
| Agent Card streaming | `true` | `false` |
| Blocking request | `message/send` waits for the final task result | `message/send` waits for the final task result after the session is connected |
| Streaming response | Supported through A2A streaming methods and SSE | Not available; clients use task polling |
| Asynchronous task | `message/send` with `configuration.blocking: false` | `message/send` with `configuration.blocking: false` |
| Task polling | `tasks/get` until the task reaches a terminal state | `tasks/get` until the task reaches a terminal state |
| Session ownership | Cloud Run service database pool | One child process and async pool per active user session |
| Task/context storage | Oracle Database | Oracle Database, with Consul routing metadata |
| Capacity control | Cloud Run instances and per-instance pool size | Gateway instances, Consul routing, worker replicas, per-session pools, and session TTL |
| Best fit | One known database/team and predictable operations | Multiple databases/teams selected dynamically from one endpoint |

Both deployments expose the public A2A endpoint at:

```text
/.well-known/agent-card.json
/a2a/jsonrpc/
```

Both accept A2A 1.0 method names and the A2A v0.3 compatibility method names.
The gateway client flow is documented in the
[gateway samples](../samples/a2a/gateway/README.md).

## Which deployment should you choose?

Choose the standalone server when the service owner controls the database
identity and team, wants clients to send prompts immediately, and benefits
from streaming responses.

Choose the gateway when one A2A endpoint must serve users selecting different
Oracle databases or teams, with isolated temporary sessions and worker-based
capacity.
9 changes: 9 additions & 0 deletions gcloud/gateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ using the internal protobuf protocol. The selected worker starts one child
runtime for that session. The child owns the database connection,
`DefaultRequestHandler`, `OracleTaskStore`, and `OracleContextStore`.

The session connection path currently accepts a DSN, username, and password.
Oracle Database wallet-based mTLS is not yet supported for these dynamic
sessions. The optional worker mTLS mode below protects the gateway-to-worker
HTTP connection; it does not provide database mTLS.

The Service Registry stores only service-discovery and non-secret
session/task-to-worker metadata. Task payloads and context mappings remain in
Oracle. Connection-form tasks are response-only bootstrap tasks: they are
Expand Down Expand Up @@ -130,6 +135,10 @@ deployment time.
Local testing does not use mTLS. The default GCloud deployment also keeps the
current private-VPC HTTP worker transport.

This mTLS mode applies only between the Cloud Run gateway and GKE workers. It
is independent of Oracle Database authentication, and does not enable wallet-
based database mTLS for gateway sessions.

For a short-lived GCloud mTLS test:

```bash
Expand Down
17 changes: 17 additions & 0 deletions samples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,23 @@ This sample intentionally omits `configuration.blocking`. The server waits
for the database work to finish and returns the completed Task in the initial
`message/send` response; no polling is needed.

## A2A dynamic gateway

The dynamic gateway samples submit the A2UI database connection form, open a
temporary worker session, and execute database tasks. The gateway advertises
`streaming: false` and supports non-blocking task execution with
`configuration.blocking: false` and `tasks/get`.

Gateway-specific samples that perform the form handshake and then execute a
real database task are in [a2a/gateway](a2a/gateway/README.md):

```bash
python samples/a2a/gateway/blocking_task.py
python samples/a2a/gateway/task_poll.py
```

See that README for local Consul, worker, and gateway startup instructions.


`SELECT_AI_DB_CONNECT_STRING` can be in any one of the following formats

Expand Down
107 changes: 107 additions & 0 deletions samples/a2a/gateway/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Dynamic A2A gateway samples

These samples connect to a dynamic gateway, submit its A2UI database
connection form, open a temporary worker session, and execute Select AI tasks
against the database.

The gateway advertises `streaming: false` and supports request/response task
operations. Long-running work is returned as a task and can be followed with
`tasks/get`.

The gateway supports asynchronous work with `message/send` and
`configuration.blocking: false`, followed by `tasks/get`.

These samples use the A2A v0.3 JSON-RPC names used by the existing samples:
`message/send`, `tasks/get`, and `tasks/cancel`. A client using A2A 1.0 should
send `A2A-Version: 1.0` and use `SendMessage`, `GetTask`, `ListTasks`, and
`CancelTask`; its non-blocking option is `configuration.returnImmediately`.
The gateway accepts both versions, but streaming is disabled in both.

## Local setup

Install the A2A extra if necessary:

```bash
source .venv/bin/activate
pip install -e '.[a2a]'
```

Run these commands in three terminals from the repository root.

Terminal 1, Consul:

```bash
consul agent -dev -bind=127.0.0.1 -client=127.0.0.1
```

Terminal 2, one worker:

```bash
source .venv/bin/activate

CONSUL_HTTP_URL=http://127.0.0.1:8500 \
WORKER_ID=local-worker \
WORKER_ADDRESS=127.0.0.1 \
WORKER_PORT=8081 \
select-ai a2a worker --host 127.0.0.1 --port 8081
```

Terminal 3, the gateway:

```bash
source .venv/bin/activate

select-ai a2a gateway \
--host 127.0.0.1 \
--port 8000 \
--agent-url http://127.0.0.1:8000 \
--consul-url http://127.0.0.1:8500
```

The worker must be able to connect to the database when a sample submits the
form. Export the same values used by the other samples, plus the optional
team name:

```bash
export SELECT_AI_DB_CONNECT_STRING='<database DSN>'
export SELECT_AI_USER='<database user>'
export SELECT_AI_PASSWORD='<database password>'
export SELECT_AI_A2A_TEAM='ORACLE_AI_DATABASE_AGENT'
```

For a TNS-alias DSN, set `TNS_ADMIN` in the worker terminal before starting
the worker. Wallet-based Oracle Database mTLS is not currently supported by
the gateway session connection path.

## Run the samples

The gateway-specific samples perform the connection-form handshake
automatically and validate that the final artifact is
`database-agent-result`.

Blocking database task:

```bash
python samples/a2a/gateway/blocking_task.py
```

Non-blocking database task with polling:

```bash
python samples/a2a/gateway/task_poll.py
```

Expected task output is similar to:

```text
Task <uuid>: completed
Artifact: database-agent-result
...
```

For a quick health check:

```bash
curl http://127.0.0.1:8081/health
curl -sS http://127.0.0.1:8000/.well-known/agent-card.json | jq
```
Loading