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 3ceba0a..0000000 Binary files a/dist/langchain_agentoracle-0.1.0-py3-none-any.whl and /dev/null differ diff --git a/dist/langchain_agentoracle-0.1.0.tar.gz b/dist/langchain_agentoracle-0.1.0.tar.gz deleted file mode 100644 index 20b0d37..0000000 Binary files a/dist/langchain_agentoracle-0.1.0.tar.gz and /dev/null differ 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 eaf1bb8..0000000 Binary files a/langchain_agentoracle/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/langchain_agentoracle/__pycache__/client.cpython-312.pyc b/langchain_agentoracle/__pycache__/client.cpython-312.pyc deleted file mode 100644 index 487541f..0000000 Binary files a/langchain_agentoracle/__pycache__/client.cpython-312.pyc and /dev/null differ diff --git a/langchain_agentoracle/__pycache__/tool.cpython-312.pyc b/langchain_agentoracle/__pycache__/tool.cpython-312.pyc deleted file mode 100644 index 70bed2d..0000000 Binary files a/langchain_agentoracle/__pycache__/tool.cpython-312.pyc and /dev/null differ 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"]