Skip to content

Commit 0d040c4

Browse files
committed
Tested with oracle-ai-database-agent-v2
- Conversation length is configurable - Use Consul session with delete behaviour for keys - Fix regression in tests
1 parent 6c93bb3 commit 0d040c4

20 files changed

Lines changed: 94 additions & 18 deletions

‎doc/source/user_guide/a2a.rst‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,8 @@ Important clustered options include:
780780
internal worker infrastructure.
781781
* ``--worker-replicas``: number of worker runtimes available for new sessions.
782782
* ``--session-ttl-seconds``: lifetime of temporary database sessions.
783+
* ``--conversation-retention-days``: number of days to retain Oracle
784+
conversations (default 7; use 0 to disable automatic removal).
783785
* ``--enable-worker-mtls``: create and use server-to-worker certificates.
784786
* ``--rotate-worker-mtls``: replace the existing test certificates and restart
785787
the worker workload.

‎doc/source/user_guide/cli.rst‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,13 @@ passing the A2UI form values to the worker.
288288
--consul-url http://127.0.0.1:8500
289289
290290
The worker options are ``--host``, ``--port``, ``--worker-id``,
291-
``--consul-url``, ``--worker-endpoint``, ``--session-ttl-seconds``, and
292-
``--session-start-timeout-seconds``. ``WORKER_ID``, ``CONSUL_HTTP_URL``, and
293-
``WORKER_ENDPOINT`` are environment fallbacks; explicit command-line values
294-
take precedence. ``--port`` is also the port registered with Consul. Cluster
291+
``--consul-url``, ``--worker-endpoint``, ``--session-ttl-seconds``,
292+
``--conversation-retention-days``, and ``--session-start-timeout-seconds``.
293+
``WORKER_ID``, ``CONSUL_HTTP_URL``, ``WORKER_ENDPOINT``, and
294+
``CONVERSATION_RETENTION_DAYS`` are environment fallbacks; explicit
295+
command-line values take precedence. Conversation retention defaults to 7 days;
296+
use 0 to keep conversations until manually deleted. ``--port`` is also the port registered with Consul.
297+
Cluster
295298
manifests can set ``WORKER_ADDRESS`` to the pod IP when no endpoint is
296299
supplied. The worker's ``--tls-cert-file``, ``--tls-key-file``, and
297300
``--tls-ca-file`` options enable gateway-to-worker mTLS; provide all three

‎gcloud/cluster/README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ password through the generated A2UI form. A session remains available for 15
66
minutes by default. Set a
77
different lifetime in seconds with `--session-ttl-seconds`; for example,
88
`--session-ttl-seconds 1800` keeps sessions for 30 minutes.
9+
Oracle conversation history is retained for 7 days by default. Set
10+
`--conversation-retention-days` to change this, or use 0 to keep
11+
conversations until they are
12+
manually deleted.
913

1014
## Protocol architecture
1115

‎gcloud/cluster/cloudbuild.yaml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ steps:
131131
sed -e "s|WORKER_IMAGE|$image|" \
132132
-e "s|WORKER_REPLICAS|${_WORKER_REPLICAS}|" \
133133
-e "s|SESSION_TTL_SECONDS|${_SESSION_TTL_SECONDS}|" \
134+
-e "s|CONVERSATION_RETENTION_DAYS|${_CONVERSATION_RETENTION_DAYS}|" \
134135
-e "s|GKE_DNS_DOMAIN|${_GKE_DNS_DOMAIN}|" \
135136
"$worker_manifest" > /workspace/worker.yaml
136137
@@ -222,6 +223,7 @@ substitutions:
222223
_SUBNET: default
223224
_WORKER_REPLICAS: "2"
224225
_SESSION_TTL_SECONDS: "900"
226+
_CONVERSATION_RETENTION_DAYS: "7"
225227
_ENABLE_WORKER_MTLS: "false"
226228
_ROTATE_WORKER_MTLS: "false"
227229
_GKE_DNS_DOMAIN: select-ai-a2a-cluster.internal

‎gcloud/cluster/deploy.sh‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ Options:
3434
--subnet NAME VPC subnet for Cloud Run direct VPC egress (default: default)
3535
--worker-replicas COUNT GKE worker replica count (default: 2)
3636
--session-ttl-seconds N Dynamic session lifetime (default: 900)
37+
--conversation-retention-days N
38+
Oracle conversation retention (default: 7; 0 keeps
39+
conversations until manually deleted)
3740
--enable-worker-mtls Use ephemeral mTLS certificates for server-to-worker calls
3841
--rotate-worker-mtls Replace the existing worker mTLS CA and certificates
3942
--mtls-cert-validity-days DAYS
@@ -59,6 +62,7 @@ network="default"
5962
subnet="default"
6063
worker_replicas="2"
6164
session_ttl_seconds="900"
65+
conversation_retention_days="7"
6266
enable_worker_mtls="false"
6367
rotate_worker_mtls="false"
6468
mtls_cert_validity_days="365"
@@ -84,6 +88,10 @@ while [[ $# -gt 0 ]]; do
8488
session_ttl_seconds="${2:?--session-ttl-seconds requires a value}"
8589
shift 2
8690
;;
91+
--conversation-retention-days)
92+
conversation_retention_days="${2:?--conversation-retention-days requires a value}"
93+
shift 2
94+
;;
8795
--enable-worker-mtls) enable_worker_mtls="true"; shift ;;
8896
--rotate-worker-mtls) rotate_worker_mtls="true"; shift ;;
8997
--mtls-cert-validity-days)
@@ -113,6 +121,10 @@ if ! [[ "$session_ttl_seconds" =~ ^[1-9][0-9]*$ ]]; then
113121
echo "--session-ttl-seconds must be a positive integer." >&2
114122
exit 2
115123
fi
124+
if ! [[ "$conversation_retention_days" =~ ^[0-9]+$ ]]; then
125+
echo "--conversation-retention-days must be a non-negative integer." >&2
126+
exit 2
127+
fi
116128
if [[ -z "$gke_dns_domain" || "$gke_dns_domain" == *.local ]]; then
117129
echo "--gke-dns-domain must be non-empty and must not end in .local." >&2
118130
exit 2
@@ -300,6 +312,7 @@ build_substitutions=(
300312
"_SUBNET=$subnet"
301313
"_WORKER_REPLICAS=$worker_replicas"
302314
"_SESSION_TTL_SECONDS=$session_ttl_seconds"
315+
"_CONVERSATION_RETENTION_DAYS=$conversation_retention_days"
303316
"_ENABLE_WORKER_MTLS=$enable_worker_mtls"
304317
"_ROTATE_WORKER_MTLS=$rotate_worker_mtls"
305318
"_GKE_DNS_DOMAIN=$gke_dns_domain"

‎gcloud/cluster/gke/worker-mtls.yaml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ spec:
3434
- https://$(POD_NAME).select-ai-a2a-worker.select-ai-a2a.svc.GKE_DNS_DOMAIN:8443
3535
- --session-ttl-seconds
3636
- "SESSION_TTL_SECONDS"
37+
- --conversation-retention-days
38+
- "CONVERSATION_RETENTION_DAYS"
3739
- --tls-cert-file
3840
- /var/run/select-ai-mtls/tls.crt
3941
- --tls-key-file

‎gcloud/cluster/gke/worker.yaml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ spec:
3434
- http://$(WORKER_ADDRESS):8080
3535
- --session-ttl-seconds
3636
- "SESSION_TTL_SECONDS"
37+
- --conversation-retention-days
38+
- "CONVERSATION_RETENTION_DAYS"
3739
ports:
3840
- name: http
3941
containerPort: 8080

‎src/select_ai/agent/a2a/context_store.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
"""Oracle Database storage for A2A-to-Oracle conversation mappings."""
99

10+
import datetime
1011
from asyncio import Lock
1112
from typing import Optional
1213

@@ -52,8 +53,12 @@ class OracleContextStore:
5253
def __init__(
5354
self,
5455
owner_resolver: OwnerResolver = resolve_user_scope,
56+
retention_days: int = 7,
5557
) -> None:
58+
if retention_days < 0:
59+
raise ValueError("retention_days must be non-negative")
5660
self.owner_resolver = owner_resolver
61+
self.retention_days = retention_days
5762
self.initialized = False
5863
self.initialize_lock = Lock()
5964

@@ -85,6 +90,7 @@ async def get_or_create(
8590
attributes=select_ai.ConversationAttributes(
8691
title=f"A2A {team_name}",
8792
description=f"A2A context {context_id}",
93+
retention_days=datetime.timedelta(days=self.retention_days),
8894
)
8995
)
9096
conversation_id = await conversation.create()

‎src/select_ai/agent/a2a/models.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class WorkerSettings:
8080
session_ttl_seconds: int
8181
session_start_timeout_seconds: int
8282
worker_endpoint: str | None = None
83+
conversation_retention_days: int = 7
8384

8485
def __post_init__(self) -> None:
8586
if self.worker_port < 1 or self.worker_port > 65_535:
@@ -90,6 +91,10 @@ def __post_init__(self) -> None:
9091
raise ValueError(
9192
"session_start_timeout_seconds must be at least 1"
9293
)
94+
if self.conversation_retention_days < 0:
95+
raise ValueError(
96+
"conversation_retention_days must be non-negative"
97+
)
9398
object.__setattr__(self, "consul_url", self.consul_url.rstrip("/"))
9499
if self.worker_endpoint:
95100
object.__setattr__(

‎src/select_ai/agent/a2a/session_process.py‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,11 @@ def __init__(
101101
self,
102102
session_ttl_seconds: int,
103103
session_start_timeout_seconds: int,
104+
conversation_retention_days: int = 7,
104105
) -> None:
105106
self.session_ttl_seconds = session_ttl_seconds
106107
self.session_start_timeout_seconds = session_start_timeout_seconds
108+
self.conversation_retention_days = conversation_retention_days
107109
self.sessions: dict[str, ChildSession] = {}
108110

109111
async def open(self, spec: SessionSpec) -> None:
@@ -122,6 +124,7 @@ async def open(self, spec: SessionSpec) -> None:
122124
spec.session_id,
123125
spec.owner,
124126
spec.team_name,
127+
self.conversation_retention_days,
125128
),
126129
daemon=True,
127130
)
@@ -322,6 +325,7 @@ def _session_process_main(
322325
session_id: str,
323326
owner: str,
324327
team_name: str,
328+
conversation_retention_days: int,
325329
) -> None:
326330
"""Entrypoint for a child that owns one Select AI database session."""
327331
try:
@@ -332,6 +336,7 @@ def _session_process_main(
332336
session_id,
333337
owner,
334338
team_name,
339+
conversation_retention_days,
335340
)
336341
)
337342
finally:
@@ -344,6 +349,7 @@ async def _run_session_process(
344349
session_id: str,
345350
owner: str,
346351
team_name: str,
352+
conversation_retention_days: int,
347353
) -> None:
348354
"""Open one async connection and execute A2A operations."""
349355
import select_ai
@@ -358,7 +364,12 @@ async def _run_session_process(
358364
)
359365
if not await select_ai.async_is_connected():
360366
raise RuntimeError("Database login failed.")
361-
runtime = SessionRuntime(session_id, owner, team_name)
367+
runtime = SessionRuntime(
368+
session_id,
369+
owner,
370+
team_name,
371+
conversation_retention_days,
372+
)
362373
await runtime.initialize()
363374
connection.send({"type": PipeMessageType.READY.value})
364375
ready = True

0 commit comments

Comments
 (0)