From c60b826a3f3d6fcd72c75e4ab54dd0e1aa3a6e41 Mon Sep 17 00:00:00 2001 From: TKCollective Date: Tue, 21 Apr 2026 17:53:33 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20v0.2.1=20=E2=80=94=20reconcile=20GitHub?= =?UTF-8?q?=20main=20with=20published=20PyPI=20surface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 0.2.0 wheel on PyPI exposes six tools, but GitHub main was still at v0.1 with a single AgentOracleTool. This commit pulls the full published source into the repo so main is the source of truth again, then applies a few targeted improvements on top. Surface (matches PyPI 0.2.0 + future 0.2.1): - AgentOracleEvaluateTool /evaluate $0.01 USDC - AgentOracleVerifyGateTool /verify-gate free (beta) - AgentOraclePreviewTool /preview free, 10/hr - AgentOracleResearchTool /research $0.02 USDC - AgentOracleDeepResearchTool /deep-research $0.10 USDC - AgentOracleBatchResearchTool /research/batch $0.02 USDC × N - get_agentoracle_tools(include_paid, include_free) convenience bundle Backwards compatibility: - AgentOracleTool (v0.1 name) remains exported, aliased to AgentOracleEvaluateTool — consistent with how 0.2.0 shipped. Targeted improvements over 0.2.0: - Prefer langchain_core.tools.BaseTool (modern LangChain import path), falling back to langchain.tools for older installs. - DEFAULT_TIMEOUT bumped 60s → 120s. Multi-source /evaluate runs 30-90s in practice; 60s was clipping tail latency during parallel invocation. - pyproject.toml: proper metadata (license, authors, classifiers, urls), deps pinned to langchain-core>=0.2.0 / pydantic>=2.0 / requests>=2.31. - .gitignore added; dist/ and egg-info/ dropped from tracking. - README rewritten to document all six tools and compatibility notes. Tested: reinstalled locally, all imports succeed, AgentOracleTool is AgentOracleEvaluateTool alias, get_agentoracle_tools() returns 6 tools with include_paid=True (default), 2 with include_paid=False. Live-tested against agentoracle.co with the tutorial's Pattern 1 payload — verdicts, adversarial tags, and corrections all return as expected. [REFUTED] substring gate fires correctly on the Bitcoin/Elon Musk claim. Ship plan after merge: tag v0.2.1, existing PyPI publish workflow fires on v* tags and uploads the new wheel. --- .gitignore | 10 + README.md | 111 ++-- ...ngchain_agentoracle-0.1.0-py3-none-any.whl | Bin 2476 -> 0 bytes dist/langchain_agentoracle-0.1.0.tar.gz | Bin 1804 -> 0 bytes langchain_agentoracle.egg-info/PKG-INFO | 25 - langchain_agentoracle.egg-info/SOURCES.txt | 10 - .../dependency_links.txt | 1 - langchain_agentoracle.egg-info/requires.txt | 1 - langchain_agentoracle.egg-info/top_level.txt | 1 - langchain_agentoracle/__init__.py | 25 +- .../__pycache__/__init__.cpython-312.pyc | Bin 239 -> 0 bytes .../__pycache__/client.cpython-312.pyc | Bin 1145 -> 0 bytes .../__pycache__/tool.cpython-312.pyc | Bin 1281 -> 0 bytes langchain_agentoracle/client.py | 15 - langchain_agentoracle/tool.py | 18 - langchain_agentoracle/tools.py | 498 ++++++++++++++++++ pyproject.toml | 26 +- 17 files changed, 616 insertions(+), 125 deletions(-) create mode 100644 .gitignore delete mode 100644 dist/langchain_agentoracle-0.1.0-py3-none-any.whl delete mode 100644 dist/langchain_agentoracle-0.1.0.tar.gz delete mode 100644 langchain_agentoracle.egg-info/PKG-INFO delete mode 100644 langchain_agentoracle.egg-info/SOURCES.txt delete mode 100644 langchain_agentoracle.egg-info/dependency_links.txt delete mode 100644 langchain_agentoracle.egg-info/requires.txt delete mode 100644 langchain_agentoracle.egg-info/top_level.txt delete mode 100644 langchain_agentoracle/__pycache__/__init__.cpython-312.pyc delete mode 100644 langchain_agentoracle/__pycache__/client.cpython-312.pyc delete mode 100644 langchain_agentoracle/__pycache__/tool.cpython-312.pyc delete mode 100644 langchain_agentoracle/client.py delete mode 100644 langchain_agentoracle/tool.py create mode 100644 langchain_agentoracle/tools.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..011fb66 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +__pycache__/ +*.pyc +*.pyo +*.egg-info/ +dist/ +build/ +.venv/ +venv/ +.env +.DS_Store diff --git a/README.md b/README.md index ca8e46a..7d66cb7 100644 --- a/README.md +++ b/README.md @@ -16,65 +16,68 @@ pip install langchain-agentoracle --- -## Quickstart +## Quickstart — gate your agent on verified claims ```python -from langchain_agentoracle import AgentOracleTool -from langchain.agents import initialize_agent, AgentType -from langchain_openai import ChatOpenAI +from langchain_agentoracle import AgentOracleEvaluateTool -llm = ChatOpenAI(model="gpt-4") +verifier = AgentOracleEvaluateTool() -tools = [AgentOracleTool()] - -agent = initialize_agent( - tools, - llm, - agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, - verbose=True +agent_output = ( + "OpenAI released GPT-4 in March 2023. " + "Bitcoin was invented by Elon Musk in 2009." ) -agent.run( - "Research the latest AI agent frameworks and verify the claims before summarizing" -) +report = verifier._run(content=agent_output, min_confidence=0.8) +print(report) + +if "[REFUTED]" in report: + print("→ Hold — one or more claims failed verification") +else: + print("→ Proceed") ``` That's it. Your agent now verifies before acting. --- -## What comes back - -```json -{ - "overall_confidence": 0.87, - "recommendation": "act", - "claims": [ - { - "claim": "LangGraph leads agent frameworks in 2026", - "verdict": "supported", - "confidence": 0.94, - "evidence": "Confirmed across 4 independent sources" - }, - { - "claim": "OpenAI acquired Anthropic in early 2026", - "verdict": "refuted", - "confidence": 0.04, - "correction": "Anthropic remains independent as of April 2026" - } - ] -} +## Tools + +| Tool | Endpoint | Cost | +|------|----------|------| +| `AgentOracleEvaluateTool` | `/evaluate` | $0.01 USDC per evaluation | +| `AgentOracleVerifyGateTool` | `/verify-gate` | Free (beta) | +| `AgentOraclePreviewTool` | `/preview` | Free, 10/hr | +| `AgentOracleResearchTool` | `/research` | $0.02 USDC | +| `AgentOracleDeepResearchTool` | `/deep-research` | $0.10 USDC (Sonar Pro) | +| `AgentOracleBatchResearchTool` | `/research/batch` | $0.02 USDC × N | + +Bundle them all into an agent: + +```python +from langchain_agentoracle import get_agentoracle_tools + +tools = get_agentoracle_tools() # all 6 tools +tools = get_agentoracle_tools(include_paid=False) # free tools only ``` --- -## Recommendation logic +## What `/evaluate` returns + +```text +EVALUATION RESULT +Overall confidence: 0.51 +Recommendation: ACT +Claims found: 3 | Supported: 2 | Refuted: 1 | Unverifiable: 0 +Sources used: sonar, sonar-pro, adversarial, gemma-4 -| Score | Recommendation | What your agent should do | -|-------|---------------|--------------------------| -| > 0.8 | `act` | Proceed — claims verified | -| 0.5–0.8 | `verify` | Pause — needs secondary check | -| < 0.5 | `reject` | Discard — evidence insufficient | +CLAIMS: + ✓ [SUPPORTED] (1.00) OpenAI released GPT-4 in March 2023 + ✗ [REFUTED] (0.83) Bitcoin was invented by Elon Musk in 2009 + Correction: Bitcoin was invented by the pseudonymous Satoshi Nakamoto. + ✓ [SUPPORTED] (1.00) The Eiffel Tower is located in Paris, France +``` --- @@ -83,7 +86,7 @@ That's it. Your agent now verifies before acting. Every evaluation runs through 4 independent sources in parallel: 1. **Sonar** — real-time web research -2. **Sonar Pro** — deep multi-step analysis +2. **Sonar Pro** — deep multi-step analysis 3. **Adversarial** — actively tries to disprove the claim 4. **Gemma 4** — claim decomposition and confidence calibration @@ -99,22 +102,32 @@ curl -X POST https://agentoracle.co/preview \ -d '{"query": "OpenAI acquired Anthropic in 2026"}' ``` -20 free requests per hour. No wallet, no API key, no account. +10 free previews per hour. No wallet, no API key, no account. --- ## Pricing -| Endpoint | Price | What it does | -|----------|-------|-------------| -| `/preview` | Free | Truncated results, no payment needed | -| `/evaluate` | $0.01/claim | Full per-claim verification + verdicts | -| `/research` | $0.02/query | Real-time research + verification | +| Endpoint | Price | +|----------|-------| +| `/preview` | Free | +| `/verify-gate` | Free (beta) | +| `/evaluate` | $0.01 USDC per evaluation | +| `/research` | $0.02 USDC per query | +| `/deep-research` | $0.10 USDC per query | +| `/research/batch` | $0.02 USDC × N queries | Payments via [x402 protocol](https://x402.org) — USDC on Base, SKALE (gasless), or Stellar. No subscriptions. No minimums. No API keys. --- +## Backwards compatibility + +- `AgentOracleTool` (v0.1 name) is kept as an alias of `AgentOracleEvaluateTool`. Existing `from langchain_agentoracle import AgentOracleTool` code continues to work — calls are now routed through the full 4-source evaluation instead of the old preview wrapper. +- Error handling hardened in 0.2.1: 402 payment-required responses return structured x402 guidance, 429 returns `retry_after`, 500+ retries with exponential backoff, and `/evaluate` now defaults to a 120s timeout to accommodate multi-source tail latency. + +--- + ## Related - [agentoracle.co](https://agentoracle.co) — main site + live demo diff --git a/dist/langchain_agentoracle-0.1.0-py3-none-any.whl b/dist/langchain_agentoracle-0.1.0-py3-none-any.whl deleted file mode 100644 index 3ceba0ace13b9aa0a96732e27d93407acf47a8bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2476 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 diff --git a/langchain_agentoracle.egg-info/PKG-INFO b/langchain_agentoracle.egg-info/PKG-INFO deleted file mode 100644 index 9e0c4b2..0000000 --- a/langchain_agentoracle.egg-info/PKG-INFO +++ /dev/null @@ -1,25 +0,0 @@ -Metadata-Version: 2.4 -Name: langchain-agentoracle -Version: 0.1.0 -Summary: LangChain-compatible integration for AgentOracle -Requires-Python: >=3.9 -Description-Content-Type: text/markdown -Requires-Dist: httpx>=0.27.0 - -# langchain-agentoracle - -LangChain-compatible integration for AgentOracle — an x402-native pay-per-query AI research API. - -## What it does - -This package lets developers query AgentOracle from Python workflows and agent systems. - -Current version supports: -- Free preview queries -- Structured preview responses -- Simple Python integration - -## Install - -```bash -pip install langchain-agentoracle diff --git a/langchain_agentoracle.egg-info/SOURCES.txt b/langchain_agentoracle.egg-info/SOURCES.txt deleted file mode 100644 index be2761c..0000000 --- a/langchain_agentoracle.egg-info/SOURCES.txt +++ /dev/null @@ -1,10 +0,0 @@ -README.md -pyproject.toml -langchain_agentoracle/__init__.py -langchain_agentoracle/client.py -langchain_agentoracle/tool.py -langchain_agentoracle.egg-info/PKG-INFO -langchain_agentoracle.egg-info/SOURCES.txt -langchain_agentoracle.egg-info/dependency_links.txt -langchain_agentoracle.egg-info/requires.txt -langchain_agentoracle.egg-info/top_level.txt \ No newline at end of file diff --git a/langchain_agentoracle.egg-info/dependency_links.txt b/langchain_agentoracle.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/langchain_agentoracle.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/langchain_agentoracle.egg-info/requires.txt b/langchain_agentoracle.egg-info/requires.txt deleted file mode 100644 index aa69c38..0000000 --- a/langchain_agentoracle.egg-info/requires.txt +++ /dev/null @@ -1 +0,0 @@ -httpx>=0.27.0 diff --git a/langchain_agentoracle.egg-info/top_level.txt b/langchain_agentoracle.egg-info/top_level.txt deleted file mode 100644 index 8e3fc5a..0000000 --- a/langchain_agentoracle.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -langchain_agentoracle diff --git a/langchain_agentoracle/__init__.py b/langchain_agentoracle/__init__.py index 22e3ff0..628dfd2 100644 --- a/langchain_agentoracle/__init__.py +++ b/langchain_agentoracle/__init__.py @@ -1,3 +1,24 @@ -from .tool import AgentOracleTool +"""LangChain integration for AgentOracle — trust verification tools for AI agents.""" +from langchain_agentoracle.tools import ( + AgentOracleTool, # legacy alias → AgentOracleEvaluateTool + AgentOracleEvaluateTool, + AgentOracleVerifyGateTool, + AgentOraclePreviewTool, + AgentOracleResearchTool, + AgentOracleDeepResearchTool, + AgentOracleBatchResearchTool, + get_agentoracle_tools, +) -__all__ = ["AgentOracleTool"] +__all__ = [ + "AgentOracleTool", + "AgentOracleEvaluateTool", + "AgentOracleVerifyGateTool", + "AgentOraclePreviewTool", + "AgentOracleResearchTool", + "AgentOracleDeepResearchTool", + "AgentOracleBatchResearchTool", + "get_agentoracle_tools", +] + +__version__ = "0.2.1" diff --git a/langchain_agentoracle/__pycache__/__init__.cpython-312.pyc b/langchain_agentoracle/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index eaf1bb88e82ae737dffb17c0aa4669cb9aa061b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 239 zcmX@j%ge<81QSwDXF3Au#~=<2FhLog#ej_I3@HpLj5!Rsj8Tk?3@J?Mj8ROL%$h7O z8G(|TjJNn5(^K%DG9g9F1 diff --git a/langchain_agentoracle/__pycache__/client.cpython-312.pyc b/langchain_agentoracle/__pycache__/client.cpython-312.pyc deleted file mode 100644 index 487541fe1e697ba29e22843bfc2164087d288509..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1145 zcmZWn&1(}u6rb6TblavjwXH&=A4)HKuqg;0N)byfQV(g-qBInSWp~?d-0Y?^8*5@p zd&nU{qH(l1n<36ELcVb!IL*Jg3yz1cDJ#l59ZDLn&11FchJ|@gMe1-1Lqw^ z=!Z~FNr?p9ngXGTEM#F96>tt?Axo~5lPozWOXx1Llp3omjtlM7TQ^R!aNz>01sr|&Ux6BA3;xQax*!|72y7R99Zok_U#`BMI2z8> zl@~oNQN7vj2ox1gIGI-r#|Zr0 z9ZpbB{oAr7SK)hSz*dzXAYILJb+csCvKVNd`Q9?`iq}O~sn}nx6nz%()YAR=hYRzM z$b9zB(&9pPnWw1XFq^FTlrUJq5~BkUt}C=xywU)biM`|swONrXmJt|CG&T-IlOWI( zu*&80(+&#~x!D}BU3A<(xL0XZ_I4UOM`M}yTOYK;n+x@wPyP4m%CVOGtc`rsMvk;G z5m#=MpFab0dbF<8R~r3btM}3ifRlyNF2T)Z7Eqp`c2J^Fo#mJTY&m?tXB2Egcn={3 z-zvF6P7<2I4JVyoa*Y(0qwQAEXq5Wxz{UY2EN+ittrZD)3s#1wMq zp&&wg%GGP>pVEucgJEe;o_do;DAbGZ?Ivjmg1*DsH{W|R?|t9S9OZHY1nhzxIln0( zKXB18B?)Nj9s~`-2%|13Q=L-6BqqC3S=MDrB=UkVWrr~JP)c^_N+qSG&v2p9C~JD2 zADDsTd*TdwAeMi!YJ0&eZd$JW)O8@XlC6nvpF>*EW=dzrlIGxXt?L#H@@#i<0f|U<4y**g>7u1-#-ZXZuA z>?)rJTd*cpTVjlnSHkTb1PxN7j9!OmTdI+D`1ad6*7sRWI&8DwmII_6TTSku%S4k( zkqT<_%7M(Ykmh-Ckt_nPDIAB46lMly0;k|tg>E454i>?94hvF__uk*ItCRhN6C6(! z8n`-Xj*RcU-Ouf+&HO}Tc7OJJe*Rm2{>$^@{K9VPWO#J%{-=*$Gc$Y`s{gJ<2{NF? z|0_T{T(_kj1-iN{6OK=fkAlEXkC{UD2UQ+z7l7ou?Up2eTJWOVO#OEP_H&URyKV?ekDVaMZw=R?+ LS~w@*;%$EbRfHTY diff --git a/langchain_agentoracle/client.py b/langchain_agentoracle/client.py deleted file mode 100644 index 8075466..0000000 --- a/langchain_agentoracle/client.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import annotations -import httpx - -PREVIEW_ENDPOINT = "https://agentoracle.co/preview" - -class AgentOracleClient: - def __init__(self): - self._http = httpx.Client(timeout=10.0) - - def query(self, question: str) -> dict: - resp = self._http.post(PREVIEW_ENDPOINT, json={"query": question}) - resp.raise_for_status() - data = resp.json() - data["preview"] = True - return data diff --git a/langchain_agentoracle/tool.py b/langchain_agentoracle/tool.py deleted file mode 100644 index c383406..0000000 --- a/langchain_agentoracle/tool.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import annotations -from .client import AgentOracleClient - -class AgentOracleTool: - name = "agentoracle_research" - description = "Research questions using AgentOracle" - - def __init__(self): - self._client = AgentOracleClient() - - def invoke(self, data: dict) -> str: - query = data["query"] - result = self._client.query(query) - return str(result) - - def run(self, query: str) -> str: - result = self._client.query(query) - return str(result) diff --git a/langchain_agentoracle/tools.py b/langchain_agentoracle/tools.py new file mode 100644 index 0000000..44dd6ec --- /dev/null +++ b/langchain_agentoracle/tools.py @@ -0,0 +1,498 @@ +""" +AgentOracle LangChain Integration +Production-grade tools for per-claim trust verification. +All endpoints, full error handling, 402 payment support, retry logic. +""" +import time +import requests +from typing import Optional, Any, Dict, List, Type +from pydantic import BaseModel, Field + +try: + # Modern LangChain (>=0.1) ships BaseTool from langchain_core + from langchain_core.tools import BaseTool +except ImportError: # pragma: no cover + from langchain.tools import BaseTool # type: ignore + +AGENTORACLE_BASE_URL = "https://agentoracle.co" +# Multi-source /evaluate can run 30-90s; keep room for tail latency. +DEFAULT_TIMEOUT = 120 +MAX_RETRIES = 3 +RETRY_BACKOFF = 2 + + +def _make_request( + endpoint: str, + payload: Dict[str, Any], + timeout: int = DEFAULT_TIMEOUT, + retries: int = MAX_RETRIES, +) -> Dict[str, Any]: + """ + Core request handler with retry logic and 402 payment awareness. + Returns structured dict — never raises on expected errors. + """ + url = f"{AGENTORACLE_BASE_URL}{endpoint}" + last_error = None + for attempt in range(retries): + try: + response = requests.post( + url, + json=payload, + timeout=timeout, + headers={"Content-Type": "application/json"}, + ) + if response.status_code == 200: + return {"success": True, "data": response.json()} + if response.status_code == 402: + return { + "success": False, + "error": "payment_required", + "message": ( + "This endpoint requires x402 payment (USDC on Base). " + "Use /preview for free access (10 req/hr), or configure " + "an x402 wallet to access paid endpoints. " + "See: agentoracle.co/.well-known/x402.json" + ), + "payment_info": response.json() if response.text else {}, + } + if response.status_code == 429: + retry_after = int(response.headers.get("X-RateLimit-Reset", 60)) + return { + "success": False, + "error": "rate_limited", + "message": f"Rate limit exceeded. Resets in {retry_after} seconds.", + "retry_after": retry_after, + } + if response.status_code == 500: + if attempt < retries - 1: + time.sleep(RETRY_BACKOFF ** attempt) + continue + return { + "success": False, + "error": "server_error", + "message": "AgentOracle server error. Retry with exponential backoff.", + } + return { + "success": False, + "error": f"http_{response.status_code}", + "message": response.text[:500], + } + except requests.Timeout: + last_error = "Request timed out" + if attempt < retries - 1: + time.sleep(RETRY_BACKOFF ** attempt) + continue + except requests.ConnectionError: + last_error = "Connection failed — check network or agentoracle.co status" + if attempt < retries - 1: + time.sleep(RETRY_BACKOFF ** attempt) + continue + except Exception as e: + return {"success": False, "error": "unexpected", "message": str(e)} + return {"success": False, "error": "max_retries", "message": last_error} + + +def _format_evaluation(data: Dict[str, Any]) -> str: + """Format /evaluate response into readable agent output.""" + ev = data.get("evaluation", {}) + if not ev: + return f"Evaluation error: {data}" + lines = [ + f"EVALUATION RESULT", + f"Overall confidence: {ev.get('overall_confidence', 0):.2f}", + f"Recommendation: {ev.get('recommendation', 'unknown').upper()}", + f"Claims found: {ev.get('total_claims', 0)} | " + f"Supported: {ev.get('verified_claims', 0)} | " + f"Refuted: {ev.get('refuted_claims', 0)} | " + f"Unverifiable: {ev.get('unverifiable_claims', 0)}", + f"Sources used: {', '.join(ev.get('sources_used', []))}", + f"Evaluation time: {data.get('meta', {}).get('evaluation_time_ms', 0)}ms | " + f"Cost: {data.get('meta', {}).get('price', '$0.01 USDC')}", + "", + "CLAIMS:", + ] + for claim in ev.get("claims", []): + verdict = claim.get("verdict", "unknown").upper() + confidence = claim.get("confidence", 0) + text = claim.get("claim", "") + evidence = claim.get("evidence", "") + correction = claim.get("correction", "") + adversarial = claim.get("adversarial_result", "") + symbol = {"SUPPORTED": "✓", "REFUTED": "✗", "UNVERIFIABLE": "?"}.get( + verdict, "?" + ) + lines.append(f" {symbol} [{verdict}] ({confidence:.2f}) {text}") + if evidence: + lines.append(f" Evidence: {evidence[:200]}") + if correction: + lines.append(f" Correction: {correction}") + if adversarial: + lines.append(f" Adversarial: {adversarial}") + gemma = data.get("gemma_calibration", {}) + if gemma: + lines.append( + f"\nGemma calibration: {gemma.get('calibrated_confidence', 0):.2f} " + f"({gemma.get('agreement', 'unknown')} agreement)" + ) + lines.append(f"\nEvaluation ID: {data.get('evaluation_id', 'unknown')}") + return "\n".join(lines) + + +def _format_research(data: Dict[str, Any]) -> str: + """Format /research or /deep-research response.""" + lines = [ + f"RESEARCH RESULT", + f"Summary: {data.get('summary', 'No summary')}", + "", + ] + facts = data.get("key_facts", []) + if facts: + lines.append("Key facts:") + for fact in facts: + lines.append(f" • {fact}") + lines.append("") + sources = data.get("sources", []) + if sources: + lines.append("Sources:") + for s in sources[:5]: + url = s.get("url", s) if isinstance(s, dict) else s + lines.append(f" • {url}") + lines.append("") + confidence = data.get("confidence_score", data.get("confidence", {}).get("score") if isinstance(data.get("confidence"), dict) else None) + if confidence is not None: + lines.append(f"Confidence: {confidence:.2f}") + meta = data.get("query_metadata", {}) + if meta: + lines.append( + f"Model: {meta.get('model', 'unknown')} | " + f"Latency: {meta.get('latency_ms', 0)}ms | " + f"Cost: ${meta.get('cost_usd', 0.02):.2f}" + ) + return "\n".join(lines) + + +# ───────────────────────────────────────────── +# INPUT SCHEMAS +# ───────────────────────────────────────────── +class EvaluateInput(BaseModel): + content: str = Field( + description=( + "Text containing claims to verify. Can be raw text, a research result, " + "news article excerpt, or any data your agent retrieved. " + "AgentOracle will decompose it into individual claims and verify each one." + ) + ) + source: Optional[str] = Field( + default="langchain", + description="Source identifier (e.g. 'exa', 'perplexity', 'web', 'langchain').", + ) + min_confidence: Optional[float] = Field( + default=0.8, + description="Minimum confidence threshold (0.0-1.0). Claims below this flag as unverifiable.", + ) + + +class PreviewInput(BaseModel): + query: str = Field( + description="Research query to run against the free preview endpoint (10 req/hr, truncated results).", + ) + + +class ResearchInput(BaseModel): + query: str = Field(description="Research query for full web research with sources and confidence score.") + tier: Optional[str] = Field( + default="standard", + description="Research tier: 'standard' ($0.02) or 'deep' for Sonar Pro ($0.10).", + ) + + +class DeepResearchInput(BaseModel): + query: str = Field( + description="Complex query requiring multi-step deep analysis via Sonar Pro.", + ) + + +class BatchResearchInput(BaseModel): + queries: List[str] = Field( + description="List of research queries to run in batch. Each query costs $0.02 USDC.", + ) + + +class VerifyGateInput(BaseModel): + content: str = Field( + description=( + "Text to run through the free pass/fail verification gate. " + "Returns a simple boolean trust decision — no per-claim breakdown. " + "Use /evaluate for full per-claim analysis." + ) + ) + threshold: Optional[float] = Field( + default=0.8, + description="Confidence threshold for pass/fail decision.", + ) + + +# ───────────────────────────────────────────── +# TOOLS +# ───────────────────────────────────────────── +class AgentOracleEvaluateTool(BaseTool): + """ + Full 4-source claim verification via AgentOracle /evaluate. + $0.01 USDC per evaluation via x402 on Base. + Returns per-claim verdicts with confidence scores and ACT/VERIFY/REJECT recommendation. + """ + + name: str = "agentoracle_evaluate" + description: str = ( + "Verify claims in any text before your agent acts on them. " + "Submits content to AgentOracle's 4-source verification pipeline " + "(Sonar + Sonar Pro + Adversarial + Gemma 4). " + "Returns per-claim verdicts (supported/refuted/unverifiable), " + "confidence scores (0.00-1.00), and a top-level recommendation: " + "ACT (>0.8), VERIFY (0.5-0.8), or REJECT (<0.5). " + "Cost: $0.01 USDC via x402. Use this before acting on retrieved data." + ) + args_schema: Type[BaseModel] = EvaluateInput + + def _run( + self, + content: str, + source: str = "langchain", + min_confidence: float = 0.8, + ) -> str: + result = _make_request( + "/evaluate", + { + "content": content, + "source": source, + "min_confidence": min_confidence, + }, + ) + if not result["success"]: + return ( + f"AgentOracle evaluation failed: {result.get('message', 'Unknown error')}\n" + f"Error type: {result.get('error', 'unknown')}\n" + f"Tip: Use agentoracle_preview for free verification (10 req/hr)." + ) + return _format_evaluation(result["data"]) + + async def _arun(self, *args: Any, **kwargs: Any) -> str: + raise NotImplementedError("Use _run — async not yet supported") + + +class AgentOraclePreviewTool(BaseTool): + """ + Free preview research via AgentOracle /preview. + 10 requests/hour. No payment required. Returns truncated results. + """ + + name: str = "agentoracle_preview" + description: str = ( + "Free research preview via AgentOracle. " + "No payment required — 10 requests per hour. " + "Returns truncated research results with confidence score. " + "Use this to test queries or when x402 payment is not configured. " + "For full results use agentoracle_research ($0.02/query)." + ) + args_schema: Type[BaseModel] = PreviewInput + + def _run(self, query: str) -> str: + result = _make_request("/preview", {"query": query}) + if not result["success"]: + return f"Preview failed: {result.get('message', 'Unknown error')}" + data = result["data"] + return ( + f"PREVIEW RESULT (truncated)\n" + f"Summary: {data.get('summary', data.get('result', 'No summary'))}\n" + f"Confidence: {data.get('confidence_score', 'N/A')}\n" + f"Note: Use agentoracle_research for full results." + ) + + async def _arun(self, *args: Any, **kwargs: Any) -> str: + raise NotImplementedError("Use _run — async not yet supported") + + +class AgentOracleResearchTool(BaseTool): + """ + Full real-time research via AgentOracle /research. + $0.02 USDC per query via x402 on Base. + Returns structured JSON with sources, confidence, key facts. + """ + + name: str = "agentoracle_research" + description: str = ( + "Real-time web research via AgentOracle. " + "Returns structured results with sources, key facts, and confidence score. " + "Powered by Perplexity Sonar. Cost: $0.02 USDC via x402. " + "For deeper analysis use agentoracle_deep_research ($0.10/query). " + "For claim verification use agentoracle_evaluate ($0.01/claim)." + ) + args_schema: Type[BaseModel] = ResearchInput + + def _run(self, query: str, tier: str = "standard") -> str: + endpoint = "/research" + payload: Dict[str, Any] = {"query": query} + if tier == "deep": + payload["tier"] = "deep" + result = _make_request(endpoint, payload) + if not result["success"]: + return f"Research failed: {result.get('message', 'Unknown error')}" + return _format_research(result["data"]) + + async def _arun(self, *args: Any, **kwargs: Any) -> str: + raise NotImplementedError("Use _run — async not yet supported") + + +class AgentOracleDeepResearchTool(BaseTool): + """ + Deep multi-step research via AgentOracle /deep-research. + $0.10 USDC per query via x402. Uses Sonar Pro for comprehensive analysis. + """ + + name: str = "agentoracle_deep_research" + description: str = ( + "Deep multi-step research via AgentOracle using Sonar Pro. " + "Best for complex questions requiring comprehensive source verification. " + "Returns extended analysis with higher confidence scoring. " + "Cost: $0.10 USDC via x402. " + "Use for due diligence, market research, or any query needing depth." + ) + args_schema: Type[BaseModel] = DeepResearchInput + + def _run(self, query: str) -> str: + result = _make_request("/deep-research", {"query": query}, timeout=90) + if not result["success"]: + return f"Deep research failed: {result.get('message', 'Unknown error')}" + return _format_research(result["data"]) + + async def _arun(self, *args: Any, **kwargs: Any) -> str: + raise NotImplementedError("Use _run — async not yet supported") + + +class AgentOracleBatchResearchTool(BaseTool): + """ + Batch research via AgentOracle /research/batch. + $0.02 USDC per query. Run multiple queries in one call. + """ + + name: str = "agentoracle_batch_research" + description: str = ( + "Run multiple research queries in a single batch call. " + "Cost: $0.02 USDC per query via x402. " + "More efficient than individual calls for 3+ queries. " + "Returns structured results for each query." + ) + args_schema: Type[BaseModel] = BatchResearchInput + + def _run(self, queries: List[str]) -> str: + if not queries: + return "No queries provided." + if len(queries) > 10: + return "Maximum 10 queries per batch. Split into smaller batches." + result = _make_request( + "/research/batch", + {"queries": queries}, + timeout=120, + ) + if not result["success"]: + return f"Batch research failed: {result.get('message', 'Unknown error')}" + data = result["data"] + results = data.get("results", []) + if not results: + return f"Batch returned no results: {data}" + lines = [f"BATCH RESEARCH RESULTS ({len(results)} queries)\n"] + for i, r in enumerate(results): + lines.append(f"Query {i+1}: {queries[i] if i < len(queries) else 'unknown'}") + lines.append(_format_research(r)) + lines.append("") + total_cost = len(queries) * 0.02 + lines.append(f"Total cost: ${total_cost:.2f} USDC") + return "\n".join(lines) + + async def _arun(self, *args: Any, **kwargs: Any) -> str: + raise NotImplementedError("Use _run — async not yet supported") + + +class AgentOracleVerifyGateTool(BaseTool): + """ + Free pass/fail verification gate via AgentOracle /verify-gate. + No payment required. Returns simple boolean trust decision. + """ + + name: str = "agentoracle_verify_gate" + description: str = ( + "Free pass/fail verification gate. " + "Quickly determine if content meets a confidence threshold. " + "Returns PASS or FAIL — no per-claim breakdown. " + "No payment required. " + "Use agentoracle_evaluate for full per-claim analysis." + ) + args_schema: Type[BaseModel] = VerifyGateInput + + def _run(self, content: str, threshold: float = 0.8) -> str: + result = _make_request( + "/verify-gate", + {"content": content, "threshold": threshold}, + ) + if not result["success"]: + return f"Verify gate failed: {result.get('message', 'Unknown error')}" + data = result["data"] + passed = data.get("passed", data.get("verified", False)) + confidence = data.get("confidence", data.get("score", 0)) + recommendation = data.get("recommendation", "verify") + return ( + f"VERIFY GATE: {'PASS' if passed else 'FAIL'}\n" + f"Confidence: {confidence:.2f}\n" + f"Recommendation: {recommendation.upper()}\n" + f"Threshold applied: {threshold}\n" + f"Use agentoracle_evaluate for per-claim breakdown." + ) + + async def _arun(self, *args: Any, **kwargs: Any) -> str: + raise NotImplementedError("Use _run — async not yet supported") + + +# ───────────────────────────────────────────── +# CONVENIENCE — get all tools at once +# ───────────────────────────────────────────── +def get_agentoracle_tools( + include_paid: bool = True, + include_free: bool = True, +) -> List[BaseTool]: + """ + Return AgentOracle tools for use with any LangChain agent. + + Args: + include_paid: Include tools that require x402 payment (evaluate, research, deep_research, batch) + include_free: Include free tools (preview, verify_gate) + + Returns: + List of LangChain BaseTool instances + + Example: + from langchain_agentoracle import get_agentoracle_tools + from langchain.agents import initialize_agent, AgentType + from langchain_openai import ChatOpenAI + + tools = get_agentoracle_tools() + agent = initialize_agent(tools, ChatOpenAI(), agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION) + agent.run("Research AI agent frameworks and verify the claims before reporting.") + """ + tools = [] + if include_free: + tools.extend([ + AgentOraclePreviewTool(), + AgentOracleVerifyGateTool(), + ]) + if include_paid: + tools.extend([ + AgentOracleEvaluateTool(), + AgentOracleResearchTool(), + AgentOracleDeepResearchTool(), + AgentOracleBatchResearchTool(), + ]) + return tools + + +# Legacy alias — keeps backward compat with 0.1.0 +AgentOracleTool = AgentOracleEvaluateTool diff --git a/pyproject.toml b/pyproject.toml index 1f7218a..2586b09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,13 +4,33 @@ build-backend = "setuptools.build_meta" [project] name = "langchain-agentoracle" -version = "0.1.0" -description = "LangChain-compatible integration for AgentOracle" +version = "0.2.1" +description = "LangChain integration for AgentOracle — trust verification tools for AI agents" readme = "README.md" requires-python = ">=3.9" +license = { text = "MIT" } +authors = [{ name = "TK Collective" }] +keywords = ["langchain", "ai", "agents", "verification", "trust", "x402", "hallucination"] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] dependencies = [ - "httpx>=0.27.0", + "langchain-core>=0.2.0", + "pydantic>=2.0", + "requests>=2.31.0", ] +[project.urls] +Homepage = "https://agentoracle.co" +Source = "https://github.com/TKCollective/langchain-agentoracle" +Issues = "https://github.com/TKCollective/langchain-agentoracle/issues" + [tool.setuptools] packages = ["langchain_agentoracle"]