Skip to content

Commit 076cd2e

Browse files
shixishclaude
andcommitted
Make DQL and ontology endpoints configurable on the client
Add dql_url and ontology_url to Diffbot and DiffbotAsync, matching the dqlUrl/ontologyUrl options in @diffbot/typescript. kg.py now reads the endpoints from the client instead of module constants, which remain the defaults. Document all client options in a README "Client configuration" section and cover its examples in test_readme_examples.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 052ebf2 commit 076cd2e

5 files changed

Lines changed: 169 additions & 5 deletions

File tree

‎README.md‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,49 @@ db = Diffbot(token=resolve_token()) # from env var or ~/.diffbot/credentials
5858
data = db.extract("https://www.example.com")
5959
```
6060

61+
### Client configuration
62+
63+
`Diffbot` and `DiffbotAsync` take the same keyword arguments — the client is the single
64+
place to configure the SDK.
65+
66+
| Argument | Default | Used by |
67+
|----------|---------|---------|
68+
| `token` | — (required) | all |
69+
| `timeout` | `30.0` (seconds) | all |
70+
| `transport` | httpx default | all |
71+
| `analyze_url` | `https://api.diffbot.com/v3` | `extract` |
72+
| `crawler_url` | `https://api.diffbot.com/v3/crawl` | `crawl`, `crawl_list_jobs`, `crawl_get_job`, `crawl_delete_job` |
73+
| `llm_url` | `https://llm.diffbot.com/rag/v1/chat/completions` | `ask`, `ask_json` |
74+
| `web_search_url` | `https://llm.diffbot.com/api/v1/web_search` | `web_search` |
75+
| `nlp_url` | `https://nl.diffbot.com/v1/` | `entities` |
76+
| `dql_url` | `https://kg.diffbot.com/kg/v3/dql` | `dql`, `dql_parallel` |
77+
| `ontology_url` | `https://kg.diffbot.com/kg/ontology` | `dql_fetch_ontology`, `dql_refresh_ontology` |
78+
79+
```python
80+
from diffbot import Diffbot
81+
82+
db = Diffbot(
83+
token="YOUR_TOKEN",
84+
timeout=60.0,
85+
dql_url="http://localhost:8080/kg/v3/dql",
86+
)
87+
```
88+
89+
`analyze_url` is a base the SDK appends `/{api}` to, and `crawler_url` is used as given
90+
for job management and with `/data` appended for crawl results. The rest are complete
91+
endpoints, used as given.
92+
93+
Passing `transport` replaces the httpx transport, which is the hook for retries, proxies,
94+
or mocking in tests. `Diffbot` takes an `httpx.BaseTransport`, `DiffbotAsync` an
95+
`httpx.AsyncBaseTransport`:
96+
97+
```python
98+
import httpx
99+
from diffbot import Diffbot
100+
101+
db = Diffbot(token="YOUR_TOKEN", transport=httpx.HTTPTransport(retries=3))
102+
```
103+
61104
### Extract structured content
62105
```python
63106
from diffbot import Diffbot

‎src/diffbot/client.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
crawl_list_jobs_async as _crawl_list_jobs_async,
2828
)
2929
from .kg import (
30+
KG_DQL_ENDPOINT,
31+
KG_ONTOLOGY_ENDPOINT,
3032
dql as _dql,
3133
dql_async as _dql_async,
3234
dql_fetch_ontology as _dql_fetch_ontology,
@@ -72,6 +74,8 @@ def __init__(
7274
crawler_url: str = CRAWL_BASE,
7375
web_search_url: str = WEB_SEARCH_BASE,
7476
nlp_url: str = NLP_BASE,
77+
dql_url: str = KG_DQL_ENDPOINT,
78+
ontology_url: str = KG_ONTOLOGY_ENDPOINT,
7579
transport: Optional[httpx.BaseTransport] = None,
7680
):
7781
if not token:
@@ -82,6 +86,8 @@ def __init__(
8286
self.crawler_url = crawler_url
8387
self.web_search_url = web_search_url
8488
self.nlp_url = nlp_url
89+
self.dql_url = dql_url
90+
self.ontology_url = ontology_url
8591
self._http = httpx.Client(
8692
timeout=timeout,
8793
headers={"User-Agent": f"diffbot-python/{__version__}"},
@@ -222,6 +228,8 @@ def __init__(
222228
crawler_url: str = CRAWL_BASE,
223229
web_search_url: str = WEB_SEARCH_BASE,
224230
nlp_url: str = NLP_BASE,
231+
dql_url: str = KG_DQL_ENDPOINT,
232+
ontology_url: str = KG_ONTOLOGY_ENDPOINT,
225233
transport: Optional[httpx.AsyncBaseTransport] = None,
226234
):
227235
if not token:
@@ -232,6 +240,8 @@ def __init__(
232240
self.crawler_url = crawler_url
233241
self.web_search_url = web_search_url
234242
self.nlp_url = nlp_url
243+
self.dql_url = dql_url
244+
self.ontology_url = ontology_url
235245
self._http = httpx.AsyncClient(
236246
timeout=timeout,
237247
headers={"User-Agent": f"diffbot-python/{__version__}"},

‎src/diffbot/kg.py‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def dql(
5151
raw: bool = False,
5252
) -> Union[Dict[str, Any], bytes]:
5353
params = _build_dql_params(client, query, size, from_, format, filter, exportspec, extra)
54-
response = client._http.get(KG_DQL_ENDPOINT, params=params)
54+
response = client._http.get(client.dql_url, params=params)
5555
client._raise_for_status(response)
5656
return response.content if raw else response.json()
5757

@@ -69,7 +69,7 @@ async def dql_async(
6969
raw: bool = False,
7070
) -> Union[Dict[str, Any], bytes]:
7171
params = _build_dql_params(client, query, size, from_, format, filter, exportspec, extra)
72-
response = await client._http.get(KG_DQL_ENDPOINT, params=params)
72+
response = await client._http.get(client.dql_url, params=params)
7373
client._raise_for_status(response)
7474
return response.content if raw else response.json()
7575

@@ -104,7 +104,7 @@ async def _one(q: Dict[str, Any]) -> Union[Dict[str, Any], bytes]:
104104

105105

106106
def dql_refresh_ontology(client: "Diffbot", dest: pathlib.Path) -> None:
107-
response = client._http.get(KG_ONTOLOGY_ENDPOINT)
107+
response = client._http.get(client.ontology_url)
108108
client._raise_for_status(response)
109109
dest.parent.mkdir(parents=True, exist_ok=True)
110110
dest.write_bytes(response.content)
@@ -116,13 +116,13 @@ def dql_fetch_ontology(client: "Diffbot") -> Ontology:
116116
Performs no caching — the caller decides whether and where to hold onto the
117117
result. Use :func:`dql_refresh_ontology` instead to persist raw bytes to disk.
118118
"""
119-
response = client._http.get(KG_ONTOLOGY_ENDPOINT)
119+
response = client._http.get(client.ontology_url)
120120
client._raise_for_status(response)
121121
return Ontology.from_json(response.content)
122122

123123

124124
async def dql_fetch_ontology_async(client: "DiffbotAsync") -> Ontology:
125125
"""Async variant of :func:`dql_fetch_ontology`."""
126-
response = await client._http.get(KG_ONTOLOGY_ENDPOINT)
126+
response = await client._http.get(client.ontology_url)
127127
client._raise_for_status(response)
128128
return Ontology.from_json(response.content)

‎tests/test_dql.py‎

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,91 @@
1+
import httpx
12
import pytest
23

4+
from diffbot import Diffbot, DiffbotAsync
5+
6+
7+
"""
8+
Endpoint configuration
9+
"""
10+
11+
12+
def test_dql_defaults_to_public_endpoint():
13+
def handler(request: httpx.Request) -> httpx.Response:
14+
assert str(request.url).startswith("https://kg.diffbot.com/kg/v3/dql")
15+
return httpx.Response(200, json={"data": []})
16+
17+
db = Diffbot(token="test-token", transport=httpx.MockTransport(handler))
18+
db.dql("type:Organization")
19+
20+
21+
def test_dql_honors_custom_dql_url():
22+
def handler(request: httpx.Request) -> httpx.Response:
23+
assert str(request.url).startswith("http://localhost:8080/kg/v3/dql")
24+
return httpx.Response(200, json={"data": []})
25+
26+
db = Diffbot(
27+
token="test-token",
28+
dql_url="http://localhost:8080/kg/v3/dql",
29+
transport=httpx.MockTransport(handler),
30+
)
31+
db.dql("type:Organization")
32+
33+
34+
def test_dql_parallel_honors_custom_dql_url():
35+
def handler(request: httpx.Request) -> httpx.Response:
36+
assert str(request.url).startswith("http://localhost:8080/kg/v3/dql")
37+
return httpx.Response(200, json={"hits": 1})
38+
39+
db = Diffbot(
40+
token="test-token",
41+
dql_url="http://localhost:8080/kg/v3/dql",
42+
transport=httpx.MockTransport(handler),
43+
)
44+
results = db.dql_parallel([{"query": "type:Organization", "size": 0}] * 2)
45+
assert results == [{"hits": 1}, {"hits": 1}]
46+
47+
48+
def test_ontology_honors_custom_ontology_url():
49+
def handler(request: httpx.Request) -> httpx.Response:
50+
assert str(request.url) == "http://localhost:8080/kg/ontology"
51+
return httpx.Response(200, json={"types": {"Organization": {"fields": {}}}})
52+
53+
db = Diffbot(
54+
token="test-token",
55+
ontology_url="http://localhost:8080/kg/ontology",
56+
transport=httpx.MockTransport(handler),
57+
)
58+
assert db.dql_fetch_ontology().types() == ["Organization"]
59+
60+
61+
@pytest.mark.anyio
62+
async def test_async_dql_honors_custom_dql_url():
63+
def handler(request: httpx.Request) -> httpx.Response:
64+
assert str(request.url).startswith("http://localhost:8080/kg/v3/dql")
65+
return httpx.Response(200, json={"data": []})
66+
67+
db = DiffbotAsync(
68+
token="test-token",
69+
dql_url="http://localhost:8080/kg/v3/dql",
70+
transport=httpx.MockTransport(handler),
71+
)
72+
await db.dql("type:Organization")
73+
74+
75+
@pytest.mark.anyio
76+
async def test_async_ontology_honors_custom_ontology_url():
77+
def handler(request: httpx.Request) -> httpx.Response:
78+
assert str(request.url) == "http://localhost:8080/kg/ontology"
79+
return httpx.Response(200, json={"types": {"Person": {"fields": {}}}})
80+
81+
db = DiffbotAsync(
82+
token="test-token",
83+
ontology_url="http://localhost:8080/kg/ontology",
84+
transport=httpx.MockTransport(handler),
85+
)
86+
ont = await db.dql_fetch_ontology()
87+
assert ont.types() == ["Person"]
88+
389

490
"""
591
Live

‎tests/test_readme_examples.py‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,31 @@
2323
)
2424

2525

26+
# ---------------------------------------------------------------------------
27+
# Client configuration
28+
# ---------------------------------------------------------------------------
29+
30+
def test_readme_client_configuration_custom_dql_url():
31+
def handler(request: httpx.Request) -> httpx.Response:
32+
assert str(request.url).startswith("http://localhost:8080/kg/v3/dql")
33+
return httpx.Response(200, json={"data": [{"entity": {"name": "Diffbot"}}]})
34+
35+
db = Diffbot(
36+
token="YOUR_TOKEN",
37+
timeout=60.0,
38+
dql_url="http://localhost:8080/kg/v3/dql",
39+
transport=httpx.MockTransport(handler),
40+
)
41+
results = db.dql('type:Organization name:"Diffbot"')
42+
assert "data" in results
43+
44+
45+
def test_readme_client_configuration_transport():
46+
transport = httpx.HTTPTransport(retries=3)
47+
with Diffbot(token="YOUR_TOKEN", transport=transport) as db:
48+
assert db._http._transport is transport
49+
50+
2651
# ---------------------------------------------------------------------------
2752
# Sync Usage
2853
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)