Metadata-Version: 2.4
Name: dynamicfeed-tools
Version: 0.1.0
Summary: Live, verifiable data tools for AI agents — Dynamic Feed client + ready-made adapters for LangChain, CrewAI, LlamaIndex and Pydantic AI.
Project-URL: Homepage, https://dynamicfeed.ai
Project-URL: Documentation, https://dynamicfeed.ai/integrations
Project-URL: Standard, https://dynamicfeed.ai/standard
Project-URL: Signing keys, https://dynamicfeed.ai/.well-known/keys
Project-URL: Source, https://github.com/dynamicfeed/df-verify
Author-email: Dynamic Feed <hello@dynamicfeed.ai>
License: MIT
License-File: LICENSE
Keywords: agents,ai,anti-hallucination,crewai,dynamic-feed,ed25519,langchain,live-data,llamaindex,provenance,pydantic-ai,tools
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.24
Provides-Extra: all
Requires-Dist: crewai>=0.55; extra == 'all'
Requires-Dist: langchain-core>=0.2; extra == 'all'
Requires-Dist: llama-index-core>=0.10; extra == 'all'
Requires-Dist: pydantic-ai>=0.2; extra == 'all'
Provides-Extra: crewai
Requires-Dist: crewai>=0.55; extra == 'crewai'
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.2; extra == 'langchain'
Provides-Extra: llamaindex
Requires-Dist: llama-index-core>=0.10; extra == 'llamaindex'
Provides-Extra: pydantic-ai
Requires-Dist: pydantic-ai>=0.2; extra == 'pydantic-ai'
Description-Content-Type: text/markdown

# dynamicfeed-tools

**Live, verifiable data tools for AI agents** — a tiny typed client for the
[Dynamic Feed](https://dynamicfeed.ai) API plus ready-made adapters for
**LangChain**, **CrewAI**, **LlamaIndex** and **Pydantic AI**.

Dynamic Feed serves 56 live-data tools across 18 verticals (weather, hazards,
security/CVEs, space, sanctions, macro, shipping, ...). Every JSON response is
**Ed25519-signed**, and every datapoint carries a **provenance envelope** —
source, licence, timestamp and age — so your agent can cite where a number came
from and you can verify it cryptographically.

```bash
pip install dynamicfeed-tools                      # client only (httpx is the sole dependency)
pip install 'dynamicfeed-tools[langchain]'         # + LangChain adapter
pip install 'dynamicfeed-tools[crewai]'            # + CrewAI adapter
pip install 'dynamicfeed-tools[llamaindex]'        # + LlamaIndex adapter
pip install 'dynamicfeed-tools[pydantic-ai]'       # + Pydantic AI adapter
```

## Keyless first

You can start with **no API key and no signup**:

```python
from dynamicfeed_tools import DynamicFeed

df = DynamicFeed()  # keyless

df.nearby_hazards(lat=-33.86, lon=151.21, radius_km=300)   # signed live-hazard scan
df.earthquakes(min_magnitude=5.0)                          # USGS, worldwide
df.reality_check("the latest Python is 3.12")              # anti-hallucination verdict
df.attest("wind_kmh", "gte", 120, lat=25.76, lon=-80.19)   # signed MET/NOT-MET attestation
df.receipt({"decision": "loan approved", "model": "gpt-4o"})  # signed compliance receipt
```

The rest of the curated surface (weather, forecasts, satellites, sanctions,
CVE lookups) takes a **free key** — one POST, no card:

```python
key = DynamicFeed.signup()           # or: curl -X POST https://dynamicfeed.ai/signup
df = DynamicFeed(api_key=key)        # or set the DYNAMICFEED_API_KEY env var

df.current_weather("Sydney")
df.weather_forecast("Tokyo", days=5)
df.satellites(group="stations")
df.sanctions_screen("ACME Trading Ltd")
df.check_vulnerability("lodash", version="4.17.10", ecosystem="npm")
```

If a key-gated method is called without a key, it raises `MissingAPIKeyError`
with the signup instructions — it never sends a doomed request.

> Want *all 56 tools* keylessly? Dynamic Feed also speaks
> [MCP](https://dynamicfeed.ai/mcp) (`https://dynamicfeed.ai/mcp`, streamable
> HTTP, no key) — ideal when your framework has an MCP adapter. This package is
> the zero-infrastructure REST path with a curated, signed subset.

## The curated tools

| Tool | What it answers | Key? |
| --- | --- | --- |
| `nearby_hazards(lat, lon, radius_km)` | live hazards around a point (quakes, fires, storms, disasters), signed | keyless |
| `attest(metric, op, threshold, lat, lon)` | signed parametric trigger: is `wind_kmh >= 120` here, right now? | keyless |
| `receipt(data)` | Ed25519-signed, timestamped record of what an AI was told/asserted | keyless |
| `earthquakes(min_magnitude, limit)` | recent worldwide earthquakes (USGS) | keyless |
| `reality_check(claim)` | fact-check a claim against live data — never guesses | keyless |
| `current_weather(city)` | live weather anywhere | free key |
| `weather_forecast(city, days)` | up to 16-day forecast | free key |
| `satellites(group, limit)` | constellation catalog + physics latency floor | free key |
| `sanctions_screen(name)` | live US OFAC SDN + UK Sanctions List screen | free key |
| `check_vulnerability(package, version, ecosystem)` | full OSV.dev advisory lookup | free key |

## LangChain / LangGraph

```python
# pip install 'dynamicfeed-tools[langchain]'
from dynamicfeed_tools.langchain import get_tools

tools = get_tools()                    # keyless tools only
tools = get_tools(api_key="dyn_...")   # all ten

from langgraph.prebuilt import create_react_agent
agent = create_react_agent("openai:gpt-4o", tools)
agent.invoke({"messages": "Any hazards within 200 km of Sydney right now?"})
```

## CrewAI

```python
# pip install 'dynamicfeed-tools[crewai]'
from crewai import Agent
from dynamicfeed_tools.crewai import get_tools

analyst = Agent(
    role="Risk analyst",
    goal="Ground every claim in live, signed data",
    backstory="Works only from verifiable sources.",
    tools=get_tools(api_key="dyn_..."),
)
```

## LlamaIndex

```python
# pip install 'dynamicfeed-tools[llamaindex]'
from llama_index.core.agent.workflow import FunctionAgent
from dynamicfeed_tools.llamaindex import get_tools

agent = FunctionAgent(tools=get_tools(), llm=llm)
await agent.run("Fact-check: 'the latest Node.js LTS is 20'.")
```

## Pydantic AI

```python
# pip install 'dynamicfeed-tools[pydantic-ai]'
from pydantic_ai import Agent
from dynamicfeed_tools.pydantic_ai import get_tools

agent = Agent("openai:gpt-4o", tools=get_tools())
result = agent.run_sync("What's shaking? Biggest earthquake in the last day.")
```

### Which tools does an agent get?

All four `get_tools()` adapters behave the same way:

* no key → only the keyless tools (an agent is never handed a tool that is
  guaranteed to fail),
* key present (argument or `DYNAMICFEED_API_KEY`) → all ten,
* `include_key_gated=True/False` overrides the auto behavior.

## Verifying responses

Responses are signed by default with the Dynamic Feed Ed25519 key. To verify:

1. fetch the public keys from <https://dynamicfeed.ai/.well-known/keys>,
2. remove the `signature` field, canonicalize the rest (JSON, sorted keys,
   compact separators), and check the Ed25519 signature over those bytes.

The full DF-VERIFY/1 spec lives at <https://dynamicfeed.ai/standard>, and the
[`dynamicfeed-verify`](https://pypi.org/project/dynamicfeed-verify/) package
does the verification in one line. Receipts and attestations can additionally
be anchored to Bitcoin via `POST /v1/anchor` for independent proof of *when*.

## Provenance envelopes

Datapoints carry their provenance — typically `source`, `licence`,
`measured_at` / `timestamp` and `age_seconds` — so an agent can cite the
upstream (USGS, NASA EONET, Open-Meteo, OFAC, OSV.dev, ...) rather than
asserting numbers from nowhere. The authoritative source catalog (publisher,
licence, commercial-use status, cadence) is keyless at
`GET https://dynamicfeed.ai/v1/sources`.

## Errors

```text
DynamicFeedError
├── MissingAPIKeyError   # key-gated method, no key configured (raised client-side)
└── APIError             # non-2xx from the API (status_code, detail)
    ├── AuthError        # 401 / 403 — missing/invalid key server-side
    └── RateLimitError   # 429 — daily quota reached
```

## Development

```bash
python3 -m venv .venv && . .venv/bin/activate
pip install -e . pytest
pytest
```

Tests run fully offline (`httpx.MockTransport`) and need none of the framework
extras; adapter construction tests activate automatically when a framework is
installed.

## License

MIT — see [LICENSE](LICENSE). Data licences vary per upstream source and are
declared per datapoint and in the [source catalog](https://dynamicfeed.ai/sources).
