remembr-proof 0.3.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.
@@ -0,0 +1,116 @@
1
+ """REMEMBR Proof Seal — Python SDK
2
+
3
+ Verify AI reasoning chains with a single function call.
4
+
5
+ Usage:
6
+ from remembr_proof import ProofSeal
7
+
8
+ seal = ProofSeal(api_key="rp_live_...")
9
+ cert = seal.verify(
10
+ output="AI's final answer",
11
+ reasoning_steps=[
12
+ {"role": "assistant", "content": "Step 1...", "confidence": 0.9},
13
+ {"role": "assistant", "content": "Step 2...", "confidence": 0.85},
14
+ ]
15
+ )
16
+
17
+ print(cert.verdict) # "verified" | "contradictions_found" | ...
18
+ print(cert.confidence) # 0.0 - 1.0
19
+ print(cert.contradictions) # list of contradiction reports
20
+ """
21
+
22
+ from .client import (
23
+ ProofSeal,
24
+ ProofCertificate,
25
+ Contradiction,
26
+ InsufficientEvidenceReason,
27
+ INSUFFICIENT_EVIDENCE_REASONS,
28
+ PolicyRejectionReason,
29
+ POLICY_REJECTION_REASONS,
30
+ ReasoningIntegrity,
31
+ ShadowReasoningIntegrity,
32
+ ShadowStats,
33
+ ShadowUnsupportedClaim,
34
+ ShadowContradiction,
35
+ ShadowLineage,
36
+ ShadowLineageTraceNode,
37
+ UnsupportedClaim,
38
+ ContradictionPair,
39
+ VerificationError,
40
+ is_insufficient_evidence_reason,
41
+ is_policy_rejection_reason,
42
+ )
43
+ from .middleware import VerifiedResponse, with_proof_seal
44
+ from .logging import configure as configure_logging, get_logger
45
+ from .agents import VerifiedAgent, VerifiedAgentResult
46
+ from .orchestrator import Orchestrator, OrchestratorResult, StageResult
47
+ from .selfdialogue import SelfDialogue, SelfDialogueResult, RoundResult
48
+ from .automation import BatchVerifier, BatchResult, RetryVerifier, ScheduledVerifier
49
+ from .watchdog import Watchdog, WatchdogStatus
50
+ from .remembr import Remembr, MemoryEntry
51
+ from .perplexity import PerplexityClient, VerifiedSearch, SearchResult, VerifiedSearchResult, Citation
52
+ from .computer import CodeRunner, VerifiedCodeRunner, ExecutionOutput, VerifiedExecutionResult
53
+ from .onyx import OnyxClient, VerifiedSpeaker, SpeechResult, VerifiedSpeechResult, SpeechRejectedError
54
+ from .upgrade import UpgradeInfo, check_for_upgrade
55
+ from .synthesis import AdversarialSynthesis, SynthesisResult
56
+
57
+ __version__ = "0.3.0"
58
+ __all__ = [
59
+ "ProofSeal",
60
+ "ProofCertificate",
61
+ "Contradiction",
62
+ "InsufficientEvidenceReason",
63
+ "INSUFFICIENT_EVIDENCE_REASONS",
64
+ "PolicyRejectionReason",
65
+ "POLICY_REJECTION_REASONS",
66
+ "ReasoningIntegrity",
67
+ "ShadowReasoningIntegrity",
68
+ "ShadowStats",
69
+ "ShadowUnsupportedClaim",
70
+ "ShadowContradiction",
71
+ "ShadowLineage",
72
+ "ShadowLineageTraceNode",
73
+ "UnsupportedClaim",
74
+ "ContradictionPair",
75
+ "VerificationError",
76
+ "is_insufficient_evidence_reason",
77
+ "is_policy_rejection_reason",
78
+ "VerifiedResponse",
79
+ "with_proof_seal",
80
+ "configure_logging",
81
+ "get_logger",
82
+ "VerifiedAgent",
83
+ "VerifiedAgentResult",
84
+ "Orchestrator",
85
+ "OrchestratorResult",
86
+ "StageResult",
87
+ "SelfDialogue",
88
+ "SelfDialogueResult",
89
+ "RoundResult",
90
+ "BatchVerifier",
91
+ "BatchResult",
92
+ "RetryVerifier",
93
+ "ScheduledVerifier",
94
+ "Watchdog",
95
+ "WatchdogStatus",
96
+ "Remembr",
97
+ "MemoryEntry",
98
+ "PerplexityClient",
99
+ "VerifiedSearch",
100
+ "SearchResult",
101
+ "VerifiedSearchResult",
102
+ "Citation",
103
+ "CodeRunner",
104
+ "VerifiedCodeRunner",
105
+ "ExecutionOutput",
106
+ "VerifiedExecutionResult",
107
+ "OnyxClient",
108
+ "VerifiedSpeaker",
109
+ "SpeechResult",
110
+ "VerifiedSpeechResult",
111
+ "SpeechRejectedError",
112
+ "UpgradeInfo",
113
+ "check_for_upgrade",
114
+ "AdversarialSynthesis",
115
+ "SynthesisResult",
116
+ ]
@@ -0,0 +1,262 @@
1
+ """OpenAI Agents SDK integration for remembr_proof.
2
+
3
+ Wraps any OpenAI Agents SDK ``Agent`` with automatic Proof Seal verification.
4
+ The ``openai-agents`` package is a soft dependency — import errors are raised
5
+ only when you actually call ``VerifiedAgent.run*``.
6
+
7
+ Usage::
8
+
9
+ from agents import Agent, function_tool
10
+ from remembr_proof import ProofSeal
11
+ from remembr_proof.agents import VerifiedAgent
12
+
13
+ @function_tool
14
+ def lookup(query: str) -> str:
15
+ \"\"\"Look up information.\"\"\"
16
+ return "result"
17
+
18
+ agent = Agent(name="Researcher", tools=[lookup])
19
+ seal = ProofSeal(api_key="rp_live_...")
20
+ va = VerifiedAgent(agent, seal)
21
+
22
+ result = va.run_sync("What is the capital of France?")
23
+ print(result.final_output)
24
+ print(result.certificate.verdict) # "verified" | ...
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from dataclasses import dataclass
30
+ import json
31
+ from typing import TYPE_CHECKING, Any, Optional
32
+
33
+ from .client import ProofCertificate, ProofSeal
34
+ from .logging import get_logger
35
+
36
+ if TYPE_CHECKING:
37
+ from agents import Agent, RunResult
38
+
39
+ log = get_logger(__name__)
40
+
41
+
42
+ # ---------------------------------------------------------------------------
43
+ # Result container
44
+ # ---------------------------------------------------------------------------
45
+
46
+ @dataclass
47
+ class VerifiedAgentResult:
48
+ """Agent run output paired with its Proof Seal certificate."""
49
+
50
+ run_result: "RunResult"
51
+ certificate: ProofCertificate
52
+
53
+ @property
54
+ def final_output(self) -> Any:
55
+ return self.run_result.final_output
56
+
57
+ @property
58
+ def is_verified(self) -> bool:
59
+ return self.certificate.is_verified
60
+
61
+ @property
62
+ def last_agent(self) -> "Agent":
63
+ return self.run_result.last_agent
64
+
65
+
66
+ # ---------------------------------------------------------------------------
67
+ # VerifiedAgent wrapper
68
+ # ---------------------------------------------------------------------------
69
+
70
+ class VerifiedAgent:
71
+ """Run an OpenAI Agents SDK agent and verify its output with Proof Seal.
72
+
73
+ Args:
74
+ agent: An ``agents.Agent`` instance (from the openai-agents package).
75
+ proof_seal: An authenticated ``ProofSeal`` client.
76
+ task_type: Optional workflow category forwarded to the Proof Seal API
77
+ (e.g. ``"general"``, ``"support_qa"``, ``"finance_ops"``).
78
+ metadata: Extra key/value pairs merged into the verification request's
79
+ metadata field alongside ``agent_name``.
80
+ shadow_extraction: When ``True``, request the shadow extractor report
81
+ without affecting the primary verdict.
82
+ policy: Optional verification policy thresholds (see
83
+ ``ProofSeal.verify`` docs).
84
+ """
85
+
86
+ def __init__(
87
+ self,
88
+ agent: "Agent",
89
+ proof_seal: ProofSeal,
90
+ *,
91
+ task_type: Optional[str] = None,
92
+ metadata: Optional[dict[str, Any]] = None,
93
+ shadow_extraction: bool = False,
94
+ policy: Optional[dict[str, Any]] = None,
95
+ ) -> None:
96
+ self._agent = agent
97
+ self._proof_seal = proof_seal
98
+ self._task_type = task_type
99
+ self._metadata = metadata or {}
100
+ self._shadow_extraction = shadow_extraction
101
+ self._policy = policy
102
+
103
+ # -- public API ----------------------------------------------------------
104
+
105
+ def run_sync(self, input: str, **runner_kwargs: Any) -> VerifiedAgentResult:
106
+ """Run the agent synchronously, then verify the output.
107
+
108
+ Args:
109
+ input: The user prompt / task description.
110
+ **runner_kwargs: Forwarded verbatim to ``Runner.run_sync()``.
111
+ """
112
+ Runner = _import_runner()
113
+ log.debug("running agent", extra={"agent": self._agent.name, "async": False})
114
+ run_result = Runner.run_sync(self._agent, input, **runner_kwargs)
115
+ return self._build_result(run_result)
116
+
117
+ async def run(self, input: str, **runner_kwargs: Any) -> VerifiedAgentResult:
118
+ """Run the agent asynchronously, then verify the output.
119
+
120
+ Args:
121
+ input: The user prompt / task description.
122
+ **runner_kwargs: Forwarded verbatim to ``Runner.run()``.
123
+ """
124
+ Runner = _import_runner()
125
+ log.debug("running agent", extra={"agent": self._agent.name, "async": True})
126
+ run_result = await Runner.run(self._agent, input, **runner_kwargs)
127
+ return self._build_result(run_result)
128
+
129
+ # -- internals -----------------------------------------------------------
130
+
131
+ def _build_result(self, run_result: "RunResult") -> VerifiedAgentResult:
132
+ final_output = _stringify_output(getattr(run_result, "final_output", ""))
133
+
134
+ reasoning_steps, tool_calls = _extract_trace(run_result)
135
+
136
+ log.debug(
137
+ "verifying output",
138
+ extra={
139
+ "agent": self._agent.name,
140
+ "reasoning_steps": len(reasoning_steps),
141
+ "tool_calls": len(tool_calls),
142
+ },
143
+ )
144
+
145
+ certificate = self._proof_seal.verify(
146
+ output=final_output,
147
+ reasoning_steps=reasoning_steps or None,
148
+ tool_calls=tool_calls or None,
149
+ task_type=self._task_type,
150
+ metadata={"agent_name": self._agent.name, "sdk": "openai-agents", **self._metadata},
151
+ policy=self._policy,
152
+ shadow_extraction=self._shadow_extraction,
153
+ )
154
+
155
+ log.info(
156
+ "verification complete",
157
+ extra={"verdict": certificate.verdict, "confidence": certificate.confidence},
158
+ )
159
+ return VerifiedAgentResult(run_result=run_result, certificate=certificate)
160
+
161
+
162
+ # ---------------------------------------------------------------------------
163
+ # Trace extraction
164
+ # ---------------------------------------------------------------------------
165
+
166
+ def _extract_trace(
167
+ run_result: "RunResult",
168
+ ) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
169
+ """Map ``RunResult.new_items`` to Proof Seal reasoning_steps and tool_calls."""
170
+ reasoning_steps: list[dict[str, Any]] = []
171
+ tool_calls: list[dict[str, Any]] = []
172
+
173
+ for item in getattr(run_result, "new_items", []):
174
+ item_type = type(item).__name__
175
+
176
+ if item_type in ("MessageOutputItem", "AgentOutputItem"):
177
+ text = _item_text(item)
178
+ if text:
179
+ reasoning_steps.append({"role": "assistant", "content": text})
180
+
181
+ elif item_type == "ToolCallItem":
182
+ tool_calls.append({
183
+ "name": _item_name(item),
184
+ "status": "skipped",
185
+ })
186
+
187
+ elif item_type == "ToolCallOutputItem":
188
+ output = _item_text(item)
189
+ target = tool_calls[-1] if tool_calls else None
190
+ if target is None:
191
+ target = {"name": _item_name(item), "status": "skipped"}
192
+ tool_calls.append(target)
193
+ target["output_summary"] = (output or "")[:500]
194
+ target["status"] = "success" if output else "skipped"
195
+
196
+ elif item_type == "HandoffOutputItem":
197
+ target = getattr(item, "target_agent", None)
198
+ label = getattr(target, "name", str(target)) if target else "agent"
199
+ reasoning_steps.append({
200
+ "role": "assistant",
201
+ "content": f"[handoff -> {label}]",
202
+ })
203
+
204
+ return reasoning_steps, tool_calls
205
+
206
+
207
+ def _stringify_output(value: Any) -> str:
208
+ if value is None:
209
+ return ""
210
+ if isinstance(value, str):
211
+ return value
212
+ if isinstance(value, (dict, list)):
213
+ return json.dumps(value, ensure_ascii=False, sort_keys=True)
214
+ return str(value)
215
+
216
+
217
+ def _item_name(item: Any) -> str:
218
+ for attr in ("name", "tool_name"):
219
+ val = getattr(item, attr, None)
220
+ if isinstance(val, str) and val:
221
+ return val
222
+
223
+ raw_item = getattr(item, "raw_item", None)
224
+ for candidate in (raw_item, getattr(raw_item, "function", None)):
225
+ val = getattr(candidate, "name", None)
226
+ if isinstance(val, str) and val:
227
+ return val
228
+
229
+ return type(item).__name__
230
+
231
+
232
+ def _item_text(item: Any) -> str:
233
+ """Best-effort text extraction from a RunItem."""
234
+ for attr in ("text", "output", "content"):
235
+ val = getattr(item, attr, None)
236
+ if isinstance(val, str) and val:
237
+ return val
238
+ if isinstance(val, dict):
239
+ text = val.get("text") or val.get("content") or val.get("output")
240
+ if isinstance(text, str) and text:
241
+ return text
242
+ return json.dumps(val, ensure_ascii=False, sort_keys=True)
243
+ if isinstance(val, list):
244
+ parts = [
245
+ p.get("text", p.get("content", "")) if isinstance(p, dict) else str(p)
246
+ for p in val
247
+ ]
248
+ joined = " ".join(p for p in parts if p)
249
+ if joined:
250
+ return joined
251
+ return ""
252
+
253
+
254
+ def _import_runner() -> Any:
255
+ try:
256
+ from agents import Runner # type: ignore[import-untyped]
257
+ return Runner
258
+ except ImportError as exc:
259
+ raise ImportError(
260
+ "openai-agents is required to use VerifiedAgent: "
261
+ "pip install openai-agents"
262
+ ) from exc