Google runs the models. You run the compliance. The gap between Vertex AI logs and regulatory evidence is where your risk lives.
v0.7.9 RELEASED – Vertex AI via OpenAI-compatible endpoint + Google ADK agent witnessing. Available on PyPI and npm.
Who this is for: GCP developers using Vertex AI or Gemini API, platform engineers managing AI workloads on Google Cloud, compliance teams building evidence pipelines for regulated AI, and auditors evaluating Google Cloud AI deployments.
The evidence gap in every Vertex AI deployment. Google provides Vertex AI audit logs, Model Garden cards, and Responsible AI practices. These cover platform-level operations. When your application calls gemini-2.0-flash for a customer-facing decision, what evidence proves that specific inference was governed at the application layer?
There are two integration paths for witnessing Google AI inference. Which one you choose depends on how your application calls the model.
| Path | SDK Method | Adapter | What It Captures | Best For |
|---|---|---|---|---|
| Vertex AI / Gemini API OpenAI-compatible endpoint |
witness.wrap(client) |
OpenAI adapter | Per-inference evidence: prompt hash, response hash, model ID, token usage, latency | Direct model inference via Vertex AI or the Gemini API |
| Google ADK Agent Agent Development Kit wrapper |
wrap_google_adk(agent, witness) |
Google ADK adapter | Agent-level evidence: input, output, model used, total execution time | ADK agent workflows with multi-step reasoning and tool use |
These paths are independent. Use one or both depending on your architecture. If you call Gemini directly for inference, use Path 1. If you run ADK agents, use Path 2. If your ADK agents call Gemini internally, Path 2 witnesses the agent boundary -- you do not need to also wrap the internal model calls.
Vertex AI now exposes an OpenAI-compatible endpoint. This means the standard OpenAI adapter works out of the box. Point the OpenAI client at the Vertex AI endpoint, wrap it with the witness, and every inference is captured.
from openai import OpenAI
from swt3_ai import Witness
witness = Witness(
endpoint="https://your-instance.example.com/api/v1/witness",
api_key="axm_live_...",
tenant_id="YOUR_TENANT",
)
client = witness.wrap(OpenAI(
base_url="https://REGION-aiplatform.googleapis.com/v1/projects/PROJECT/locations/REGION/endpoints/openapi",
api_key="YOUR_GCP_TOKEN",
))
response = client.chat.completions.create(
model="google/gemini-2.0-flash",
messages=[{"role": "user", "content": "Classify this support ticket."}],
)
witness.flush()
import OpenAI from 'openai';
import { Witness } from '@tenova/swt3-ai';
const witness = new Witness({
endpoint: 'https://your-instance.example.com/api/v1/witness',
apiKey: 'axm_live_...',
tenantId: 'YOUR_TENANT',
});
const client = witness.wrap(new OpenAI({
baseURL: 'https://REGION-aiplatform.googleapis.com/v1/projects/PROJECT/locations/REGION/endpoints/openapi',
apiKey: 'YOUR_GCP_TOKEN',
}));
const response = await client.chat.completions.create({
model: 'google/gemini-2.0-flash',
messages: [{ role: 'user', content: 'Classify this support ticket.' }],
});
await witness.flush();
Why this works: Vertex AI's OpenAI-compatible endpoint accepts the same request format as OpenAI's API. The SWT3 OpenAI adapter intercepts the call transparently. The provider tag in the witness record will read openai (since the client is an OpenAI SDK instance), but the model_id field captures the actual model name -- google/gemini-2.0-flash -- which is the value that matters for audit purposes.
model_id: google/gemini-2.0-flash -- the Gemini model name exactly as passed to the API.
prompt_hash: SHA-256 of the prompt content. Proves what was sent without storing the content.
response_hash: SHA-256 of the model response. Proves what was received without storing the content.
latency_ms: Wall-clock time for the inference call.
The anchor captures the Gemini model version even though the underlying client is the OpenAI SDK. Verify the model_id field contains the expected Gemini model identifier (e.g., google/gemini-2.0-flash). If it shows a generic name like gpt-4, the application is not correctly specifying the Vertex AI model -- this is a configuration finding, not a witness protocol issue.
For developers using the Gemini API directly (not through Vertex AI), Google also provides an OpenAI-compatible endpoint at generativelanguage.googleapis.com. The same pattern applies -- point the OpenAI client at the Gemini endpoint and wrap it.
from openai import OpenAI
from swt3_ai import Witness
witness = Witness(
endpoint="https://your-instance.example.com/api/v1/witness",
api_key="axm_live_...",
tenant_id="YOUR_TENANT",
)
client = witness.wrap(OpenAI(
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
api_key="YOUR_GEMINI_KEY",
))
response = client.chat.completions.create(
model="gemini-2.0-flash",
messages=[{"role": "user", "content": "Summarize this contract clause."}],
)
witness.flush()
import OpenAI from 'openai';
import { Witness } from '@tenova/swt3-ai';
const witness = new Witness({
endpoint: 'https://your-instance.example.com/api/v1/witness',
apiKey: 'axm_live_...',
tenantId: 'YOUR_TENANT',
});
const client = witness.wrap(new OpenAI({
baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/',
apiKey: 'YOUR_GEMINI_KEY',
}));
const response = await client.chat.completions.create({
model: 'gemini-2.0-flash',
messages: [{ role: 'user', content: 'Summarize this contract clause.' }],
});
await witness.flush();
Vertex AI vs. Gemini API: The Vertex AI endpoint routes through your GCP project and inherits its IAM, VPC, and audit log configuration. The Gemini API endpoint is a standalone API with its own key. Both produce identical witness anchors. Choose based on your organization's infrastructure requirements -- Vertex AI for enterprise GCP environments, Gemini API for simpler deployments.
Both the Vertex AI and Gemini API paths use the OpenAI-compatible message format, which means system prompts are role: "system" messages in the messages array. The adapter extracts and hashes them independently as system_prompt_hash -- the same behavior as direct OpenAI usage.
response = client.chat.completions.create(
model="google/gemini-2.0-flash",
messages=[
{"role": "system", "content": "You are a document classifier for regulated industries."},
{"role": "user", "content": "Classify this filing."},
],
)
# system_prompt_hash captures the classifier instructions for this call
If your system prompt changes between deployments, the hash changes automatically. This creates drift evidence for Gemini deployments without any additional configuration.
Query the ledger for system_prompt_hash values on Gemini inference anchors. A stable hash across the assessment period proves the system prompt was consistent. A hash change proves it was modified -- cross-reference with the organization's change management records. This works identically for both Vertex AI and Gemini API paths.
The Agent Development Kit (ADK) is Google's framework for building multi-step AI agents. ADK agents may chain multiple model calls, use tools, and maintain state across turns. The ADK adapter witnesses the agent-level execution -- input in, result out -- rather than individual LLM calls within the agent loop.
from swt3_ai import Witness
from swt3_ai.adapters.google_adk import wrap_google_adk
witness = Witness(
endpoint="https://your-instance.example.com/api/v1/witness",
api_key="axm_live_...",
tenant_id="YOUR_TENANT",
agent_id="claims-processor",
)
agent = wrap_google_adk(your_adk_agent, witness, model_id="gemini-2.0-flash")
result = agent.run("Process this customer request")
witness.flush()
import { Witness, wrapGoogleADK } from '@tenova/swt3-ai';
const witness = new Witness({
endpoint: 'https://your-instance.example.com/api/v1/witness',
apiKey: 'axm_live_...',
tenantId: 'YOUR_TENANT',
agentId: 'claims-processor',
});
const agent = wrapGoogleADK(yourAdkAgent, witness, { modelId: 'gemini-2.0-flash' });
const result = await agent.run('Process this customer request');
await witness.flush();
Why agent-level witnessing matters: ADK agents may invoke Gemini multiple times internally -- once to reason about the task, again to select a tool, again to interpret the tool's output. Witnessing each internal call would produce noise without compliance value. The ADK adapter witnesses the outer boundary: what went in, what came out, which model powered it, and how long the full execution took. This is the evidence surface that auditors need.
Each ADK agent invocation produces a single anchor covering the full agent execution: prompt in, result out, model used, total wall-clock latency.
The agent_id field (e.g., claims-processor) links the anchor to the specific agent in your deployment. This allows auditors to trace a decision back to the responsible agent without parsing internal reasoning traces.
The distinction between model-level and agent-level evidence matters. Model-level evidence (from witness.wrap()) proves a single LLM call happened. Agent-level evidence (from wrap_google_adk()) proves an autonomous workflow completed. For regulatory purposes, agent-level evidence is often more relevant because it maps to a business decision, not an infrastructure call. If the organization uses both paths, verify that agent anchors have agent_id set and model-only anchors do not -- this prevents double-counting in compliance reporting.
Full ADK integration details: For tool witnessing within ADK agents, multi-turn session handling, and advanced configuration, see the dedicated Google ADK Witnessing Guide.
Vertex AI Cloud Audit Logs and SWT3 witness anchors serve different purposes. Neither replaces the other. Together, they form a complete evidence chain from infrastructure to application.
| Evidence Dimension | Vertex AI Audit Logs | SWT3 Witness Anchors |
|---|---|---|
| Who called the model | IAM principal, service account | Not captured (infrastructure concern) |
| Which API was called | Method, endpoint, parameters | Not captured (infrastructure concern) |
| What was sent to the model | Not logged (data residency) | SHA-256 hash of prompt content |
| What the model returned | Not logged (data residency) | SHA-256 hash of response content |
| Which model version | Model resource name | model_id in anchor |
| Latency and token usage | Partial (request latency only) | Wall-clock latency + token counts |
| Tamper-evident record | Mutable log sink | Cryptographic fingerprint, independently verifiable |
| Cross-provider portability | GCP-only | Same anchor format across all providers |
Vertex AI audit logs answer "who called what API and when." SWT3 anchors answer "what did the AI decide and can you prove it." An auditor evaluating an AI deployment needs both layers. The audit log proves the infrastructure was used correctly. The witness anchor proves the AI decision was governed.
Request the Vertex AI Cloud Audit Log export for the assessment period alongside the SWT3 ledger export. Cross-reference timestamps: every PredictRequest or GenerateContent entry in the audit log should have a corresponding SWT3 anchor within a 5-second window. If audit log entries exist without matching anchors, the application has an unwitnessed inference path -- a gap in the evidence chain. If anchors exist without matching audit log entries, the witness is recording calls from a non-GCP endpoint (e.g., Gemini API direct), which is valid but should match the organization's stated architecture.
Every witness anchor minted on Vertex AI or Gemini maps to specific regulatory obligations. This table shows which SWT3 procedure satisfies each requirement and what evidence the anchor provides.
| Obligation | Regulation | SWT3 Evidence | Procedure |
|---|---|---|---|
| Automatic logging of AI system operation | EU AI Act Art. 12 | Per-decision fingerprint with timestamp, model ID, and content hashes | AI-INF.1 |
| Transparency of AI output | EU AI Act Art. 13 | Response hash proves what the model returned at decision time | AI-INF.1 |
| Deployer obligation to monitor | EU AI Act Art. 26 | Continuous anchor stream proves ongoing monitoring is operational | AI-INF.1 |
| Document AI interfaces and dependencies | NIST AI RMF MAP 1.5 | Model ID, provider, agent_id, and latency in every anchor | AI-INF.1 |
| Track deployed AI behavior | NIST AI RMF MN 2.2 | Continuous decision-level tracking via anchor stream | AI-INF.1 |
| AI management system monitoring | ISO 42001 Clause 9.1 | Witness anchors as monitoring evidence for AI management system performance | AI-INF.1 |
| AI risk treatment evidence | ISO 42001 Clause 8.3 | Anchor density and refusal rates demonstrate risk controls are active | AI-GRD.1 |
| Agent identity and accountability | NIST AI RMF GV 1.3 | agent_id field on ADK anchors links decisions to specific agents | AI-ID.1 |
Framework-specific crosswalks: For detailed mappings beyond this summary table, see the Google Gemini Agent Platform Crosswalk, the EU AI Act Compliance Crosswalk, and the NIST AI RMF Crosswalk.
Three lines to compliance evidence. Create the witness, wrap the client, flush when done. Every Gemini inference is now witnessed.
# Python
pip install swt3-ai openai
# TypeScript
npm install @tenova/swt3-ai openai
from openai import OpenAI
from swt3_ai import Witness
witness = Witness(
endpoint="https://your-instance.example.com/api/v1/witness",
api_key="axm_live_...",
tenant_id="YOUR_TENANT",
)
client = witness.wrap(OpenAI(
base_url="https://REGION-aiplatform.googleapis.com/v1/projects/PROJECT/locations/REGION/endpoints/openapi",
api_key="YOUR_GCP_TOKEN",
))
response = client.chat.completions.create(
model="google/gemini-2.0-flash",
messages=[{"role": "user", "content": "Classify this document."}],
)
witness.flush()
Four lines added to your existing code. Zero changes to application logic. Every Gemini inference now has a cryptographic witness anchor that survives independently of Google Cloud infrastructure.
| If You Have | Start With | Lines of Code |
|---|---|---|
| Vertex AI with OpenAI-compat endpoint | witness.wrap(OpenAI(base_url=vertex_url)) |
1 |
| Gemini API direct | witness.wrap(OpenAI(base_url=gemini_url)) |
1 |
| Google ADK agents | wrap_google_adk(agent, witness) |
1 |