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.
- remembr_proof/__init__.py +116 -0
- remembr_proof/agents.py +262 -0
- remembr_proof/automation.py +348 -0
- remembr_proof/client.py +565 -0
- remembr_proof/computer.py +344 -0
- remembr_proof/logging.py +83 -0
- remembr_proof/middleware.py +247 -0
- remembr_proof/onyx.py +371 -0
- remembr_proof/orchestrator.py +260 -0
- remembr_proof/perplexity.py +373 -0
- remembr_proof/remembr.py +411 -0
- remembr_proof/selfdialogue.py +292 -0
- remembr_proof/synthesis.py +354 -0
- remembr_proof/upgrade.py +148 -0
- remembr_proof/watchdog.py +235 -0
- remembr_proof-0.3.0.dist-info/METADATA +361 -0
- remembr_proof-0.3.0.dist-info/RECORD +19 -0
- remembr_proof-0.3.0.dist-info/WHEEL +5 -0
- remembr_proof-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -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
|
+
]
|
remembr_proof/agents.py
ADDED
|
@@ -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
|