dcp_ai.openai — OpenAI Integration¶
DCP wrapper for the OpenAI Python client. Creates an intent for each chat call and records an audit entry when the call returns, offers DCP tools for function calling, and keeps the audit trail hash-chained.
The wrapper keeps unsigned records and evaluates no policy: every call runs, and its audit entry records policy_decision: "approved". The secret_key it accepts is not used. For signed bundles and a policy gate around function calls, follow docs/QUICKSTART_OPENAI.md.
Supported DCP Specifications¶
DCP-01 through DCP-09, partially: unsigned records and drafts in the shapes of these specifications, and bundle
verification through dcp_verify_bundle. It does not implement them in full.
Installation¶
Published on PyPI (26/10/2026). dcp-ai 2.10.0 includes the implementation described here. Upgrade from 2.9.0 and refresh your environment or lockfile. See Python release notes.
Version 2.9.0 routes every bundle to V1. Version 2.10.0 verifies V2 and enforces verifier policy for both versions, including the asynchronous averify_bundle_tool_call entry point. Tool descriptions and audit records alone are not signed evidence or authorization.
For source development:
git clone https://github.com/dcp-ai-protocol/dcp-ai.git
pip install -e "/path/to/dcp-ai/sdks/python[openai]"
Quickstart¶
from openai import OpenAI
from dcp_ai.openai import DCPOpenAIClient
client = OpenAI() # reads OPENAI_API_KEY from the environment
# The wrapper reads agent_id from the passport and human_id from the RPR;
# it does not check or sign them.
passport = {"agent_id": "agent:gpt-assistant-001", "capabilities": ["browse", "api_call"], "risk_tier": "medium"}
rpr = {"human_id": "rpr:human-001"}
dcp_client = DCPOpenAIClient(openai_client=client, passport=passport, rpr=rpr)
# Without tools of your own, the DCP tools are added (auto_intent=True), so the
# model may answer with tool calls instead of text.
response = dcp_client.chat_completions_create(
model="gpt-4",
messages=[{"role": "user", "content": "Summarize today's news"}],
)
message = response.choices[0].message
print(message.content or message.tool_calls)
for entry in dcp_client.get_audit_trail():
print(f"[{entry['timestamp']}] {entry['intent_id']} -> {entry['outcome']}")
API Reference¶
DCPOpenAIClient¶
DCPOpenAIClient(
openai_client: Any, # OpenAI() instance
passport: dict[str, Any], # agent_id and, for commission, capabilities and risk_tier are read
rpr: dict[str, Any], # human_id is read
secret_key: str = "", # Accepted but not used: nothing is signed
session_nonce: str = "", # A fresh one when empty
auto_intent: bool = True, # Adds DCP_TOOLS to calls that pass no tools
)
Methods¶
| Method | Description |
|---|---|
chat_completions_create(**kwargs) |
Creates an intent, calls client.chat.completions.create(**kwargs) and, if it returns, records an audit entry. A call that raises propagates its exception and leaves no audit entry. |
commission(purpose, capabilities?) |
Commission the agent (DCP-05 §3.1); returns an unsigned certificate and records an audit entry. The certificate and the commissioned state are local: nothing is attested or sent elsewhere. |
get_audit_trail() |
The audit entries, hash-chained: the in-memory list itself, not a copy, so changing it changes the trail. |
get_session_nonce() |
The session nonce. |
chat_completions_create Flow¶
- Creates an intent:
action_type: "api_call", channelapi,estimated_impact: "low",data_classes: ["none"]. - Adds
DCP_TOOLSwhenauto_intentis on and the call passes notools. - Executes
client.chat.completions.create(). No policy decision is made: the call always runs. - If the call returns, records an audit entry with
policy_decision: "approved"and the responseidasevidence.result_ref, chained to the previous entry.
DCP_TOOLS¶
OpenAI function calling tool definitions for DCP:
# Fragment: client as in the Quickstart
from dcp_ai.openai import DCP_TOOLS
messages = [{"role": "user", "content": "Declare your intent to make an API call"}]
response = client.chat.completions.create(
model="gpt-4",
messages=messages,
tools=DCP_TOOLS,
)
Available Tools¶
| Tool | Parameters | Description |
|---|---|---|
dcp_verify_bundle |
signed_bundle_json: string |
Verifies a DCP Signed Bundle |
dcp_declare_intent |
action_type: enum, target_channel: enum, data_classes?: string[], estimated_impact?: enum |
Declares intent before an action (DCP-02) |
dcp_commission_agent |
purpose: string, capabilities?: string[], risk_tier?: enum |
Commission agent (DCP-05 §3.1) |
dcp_report_vitality |
task_completion_rate, error_rate, human_satisfaction, policy_alignment |
Report vitality metrics (DCP-05 §4.1) |
dcp_declare_rights |
rights: string[], jurisdiction: string |
Declare agent rights (DCP-08 §3.1) |
dcp_create_mandate |
human_id: string, authority_scope: string[], valid_from?: string, valid_until?: string |
Create delegation mandate (DCP-09 §3.1) |
action_type: browse, api_call, send_email, create_calendar_event, initiate_payment, update_crm, write_file, execute_code
target_channel: web, api, email, calendar, payments, crm, filesystem, runtime
estimated_impact: low, medium, high
handle_dcp_tool_call(tool_name, arguments, session_nonce="", verifier_policy=None)¶
Processes DCP tool calls returned by OpenAI and returns a JSON string. Apart from dcp_verify_bundle, the tools return unsigned drafts (for example dcp_declare_intent returns {"intent_declared": true, "intent": {...}} with agent_id and human_id set to "self"). These drafts are not normative artifacts ready to sign: "self" is shorter
than the 6 characters the schemas require for agent_id and human_id, and the certificate, rights and mandate drafts
lack fields of their schemas. Adapt them, validate them against the schemas and sign them with the core SDK.
dcp_verify_bundle verifies a V2 bundle with verify_signed_bundle_v2 from dcp_ai.v2 under
verifier_policy: a dict of VerifierPolicy fields set over DEFAULT_VERIFIER_POLICY (a
default_mode without risk_overrides applies to every risk tier), or a full VerifierPolicy;
the default is DEFAULT_VERIFIER_POLICY. A V1 bundle, which has an Ed25519 signature only, no
intended_verifier and no session window, is accepted only when the policy has allow_v1_bundles,
accepts ed25519 (accepted_classical_algs, no advisory_rejected_algs entry), requires no
audience binding, session expiry or maximum session duration, and would accept a classical-only
bundle for its risk tier and security tier (spec/VERIFICATION.md V2-10); its artifacts must be
objects, its passport agent_id and RPR human_id strings. The result is JSON with
verified, dcp_version, the verifier's errors and warnings (at most 20 each, of 500
characters) and, for V2, its details (each value bounded the same way), with the control and
format characters of bundle-supplied text escaped. It works with or without a running event loop,
and leaves the thread's current event loop as it was; async code can await
averify_bundle_tool_call(arguments, verifier_policy) instead.
dcp_verify_bundle is cryptographic verification under the policy your code passes: verifier_policy is an
argument of your call, never read from the model's arguments. It is not admission of an anchored principal and
authorizes no action (see docs/ADMISSION.md).
# Fragment: response is a chat completion with tool calls
import json
from dcp_ai.openai import handle_dcp_tool_call
for tool_call in response.choices[0].message.tool_calls or []:
if tool_call.type == "function" and tool_call.function.name.startswith("dcp_"):
result = handle_dcp_tool_call(
tool_call.function.name,
json.loads(tool_call.function.arguments),
)
print(result) # JSON with the result
Advanced Example — Function Calling with DCP¶
# Fragment: passport and rpr as in the Quickstart
import json
from openai import OpenAI
from dcp_ai.openai import DCPOpenAIClient, handle_dcp_tool_call
dcp_client = DCPOpenAIClient(openai_client=OpenAI(), passport=passport, rpr=rpr)
# No tools passed: the DCP tools are added, so the model can call them
response = dcp_client.chat_completions_create(
model="gpt-4",
messages=[{"role": "user", "content": "Declare your intent to make an API call"}],
)
for tc in response.choices[0].message.tool_calls or []:
if tc.type == "function":
result = handle_dcp_tool_call(tc.function.name, json.loads(tc.function.arguments))
print(f"Tool: {tc.function.name} -> {result}")
Development¶
From a checkout:
Dependencies¶
dcp-ai— DCP SDK (crypto, merkle, verify, models)openai— Official OpenAI client
License¶
Apache-2.0