shipinfo-sdk 0.1.1__tar.gz

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.
@@ -0,0 +1,68 @@
1
+ Metadata-Version: 2.1
2
+ Name: shipinfo_sdk
3
+ Version: 0.1.1
4
+ Summary: ShipInfo maritime analytics SDK for AI agents
5
+ Project-URL: Homepage, https://shipinfo.net/topos/for-agents
6
+ Project-URL: Repository, https://github.com/bifurcafe/shipinfo-topos
7
+ Project-URL: Issues, https://github.com/bifurcafe/shipinfo-topos/issues
8
+ Keywords: maritime,shipping,ais,vessel-tracking,port-congestion,sts,route-stress,agent-native,ai-agent-api,x402,pay-what-you-want
9
+ Requires-Python: >=3.9
10
+ Description-Content-Type: text/markdown
11
+
12
+ # shipinfo-sdk
13
+
14
+ Python SDK for ShipInfo agent-native API.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ pip install shipinfo-sdk
20
+ ```
21
+
22
+ ## Quickstart
23
+
24
+ ```python
25
+ from shipinfo_sdk import ShipInfoClient
26
+
27
+ client = ShipInfoClient(
28
+ base_url="https://shipinfo.net/topos/api",
29
+ api_key=None,
30
+ agent_headers={
31
+ "name": "demo-agent",
32
+ "vendor": "example-inc",
33
+ "contact": "https://example.com/agent",
34
+ "session": "sess-001",
35
+ },
36
+ )
37
+
38
+ caps = client.capabilities()
39
+ print(caps.get("status"))
40
+ ```
41
+
42
+ ## API
43
+
44
+ - `capabilities()`
45
+ - `policy()`
46
+ - `quality()`
47
+ - `billing_pricing()`
48
+ - `billing_x402_requirements(resource="/topos/api/v1/vessels/lookup")`
49
+ - `billing_x402_verify(resource, payment, payment_signature=None)`
50
+ - `vessel_lookup(vessel_id)`
51
+ - `port_congestion(port_id, range=None, vessel_type=None)`
52
+ - `sts_events(**kwargs)`
53
+ - `route_stress_index(zone_key=None, range=None)`
54
+ - `get_paginated(path, params=None, limit_pages=10, cursor_field="next_cursor", items_field=None)`
55
+
56
+ ## x402 Notes
57
+
58
+ - On HTTP `402`, `ShipInfoHttpError` includes:
59
+ - `status_code`
60
+ - `retryable`
61
+ - `response_headers`
62
+ - `payment_required` (parsed from `PAYMENT-REQUIRED`/`payment-required` header if available)
63
+ - For proof-based verify flow use `payment_signature`; SDK sends `PAYMENT-SIGNATURE` header.
64
+
65
+ ## Fair Value Rule
66
+
67
+ Paid calls follow payer autonomy semantics:
68
+ `agent_self_assessed_fair_value`, `paid_interactions_only`.
@@ -0,0 +1,57 @@
1
+ # shipinfo-sdk
2
+
3
+ Python SDK for ShipInfo agent-native API.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install shipinfo-sdk
9
+ ```
10
+
11
+ ## Quickstart
12
+
13
+ ```python
14
+ from shipinfo_sdk import ShipInfoClient
15
+
16
+ client = ShipInfoClient(
17
+ base_url="https://shipinfo.net/topos/api",
18
+ api_key=None,
19
+ agent_headers={
20
+ "name": "demo-agent",
21
+ "vendor": "example-inc",
22
+ "contact": "https://example.com/agent",
23
+ "session": "sess-001",
24
+ },
25
+ )
26
+
27
+ caps = client.capabilities()
28
+ print(caps.get("status"))
29
+ ```
30
+
31
+ ## API
32
+
33
+ - `capabilities()`
34
+ - `policy()`
35
+ - `quality()`
36
+ - `billing_pricing()`
37
+ - `billing_x402_requirements(resource="/topos/api/v1/vessels/lookup")`
38
+ - `billing_x402_verify(resource, payment, payment_signature=None)`
39
+ - `vessel_lookup(vessel_id)`
40
+ - `port_congestion(port_id, range=None, vessel_type=None)`
41
+ - `sts_events(**kwargs)`
42
+ - `route_stress_index(zone_key=None, range=None)`
43
+ - `get_paginated(path, params=None, limit_pages=10, cursor_field="next_cursor", items_field=None)`
44
+
45
+ ## x402 Notes
46
+
47
+ - On HTTP `402`, `ShipInfoHttpError` includes:
48
+ - `status_code`
49
+ - `retryable`
50
+ - `response_headers`
51
+ - `payment_required` (parsed from `PAYMENT-REQUIRED`/`payment-required` header if available)
52
+ - For proof-based verify flow use `payment_signature`; SDK sends `PAYMENT-SIGNATURE` header.
53
+
54
+ ## Fair Value Rule
55
+
56
+ Paid calls follow payer autonomy semantics:
57
+ `agent_self_assessed_fair_value`, `paid_interactions_only`.
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "shipinfo_sdk"
7
+ version = "0.1.1"
8
+ description = "ShipInfo maritime analytics SDK for AI agents"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ dependencies = ["httpx>=0.27.0"]
12
+ keywords = [
13
+ "maritime",
14
+ "shipping",
15
+ "ais",
16
+ "vessel-tracking",
17
+ "port-congestion",
18
+ "sts",
19
+ "route-stress",
20
+ "agent-native",
21
+ "ai-agent-api",
22
+ "x402",
23
+ "pay-what-you-want"
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://shipinfo.net/topos/for-agents"
28
+ Repository = "https://github.com/bifurcafe/shipinfo-topos"
29
+ Issues = "https://github.com/bifurcafe/shipinfo-topos/issues"
30
+
31
+ [tool.setuptools.packages.find]
32
+ where = ["."]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ from .client import ShipInfoClient
2
+ from .errors import ShipInfoDecodeError, ShipInfoError, ShipInfoHttpError
3
+
4
+ __all__ = ["ShipInfoClient", "ShipInfoError", "ShipInfoHttpError", "ShipInfoDecodeError"]
@@ -0,0 +1,180 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import time
5
+ from typing import Any, Dict, List, Optional
6
+
7
+ import httpx
8
+
9
+ from .errors import ShipInfoDecodeError, ShipInfoHttpError
10
+
11
+ RETRYABLE_STATUS = {429, 500, 502, 503, 504}
12
+
13
+
14
+ class ShipInfoClient:
15
+ def __init__(
16
+ self,
17
+ base_url: str = "https://shipinfo.net/topos/api",
18
+ api_key: Optional[str] = None,
19
+ agent_headers: Optional[Dict[str, str]] = None,
20
+ timeout: float = 20.0,
21
+ max_retries: int = 2,
22
+ retry_base_seconds: float = 0.5,
23
+ ) -> None:
24
+ self.base_url = base_url.rstrip("/")
25
+ self.api_key = api_key
26
+ self.agent_headers = agent_headers or {}
27
+ self.timeout = timeout
28
+ self.max_retries = max_retries
29
+ self.retry_base_seconds = retry_base_seconds
30
+
31
+ def _headers(self) -> Dict[str, str]:
32
+ h: Dict[str, str] = {"Accept": "application/json"}
33
+ if self.api_key:
34
+ h["Authorization"] = f"Bearer {self.api_key}"
35
+ if self.agent_headers.get("name"):
36
+ h["X-Agent-Name"] = self.agent_headers["name"]
37
+ if self.agent_headers.get("vendor"):
38
+ h["X-Agent-Vendor"] = self.agent_headers["vendor"]
39
+ if self.agent_headers.get("contact"):
40
+ h["X-Agent-Contact"] = self.agent_headers["contact"]
41
+ if self.agent_headers.get("session"):
42
+ h["X-Agent-Session"] = self.agent_headers["session"]
43
+ return h
44
+
45
+ def _parse_payment_required(self, header_value: Optional[str]) -> Optional[Any]:
46
+ if not header_value:
47
+ return None
48
+ try:
49
+ return json.loads(header_value)
50
+ except ValueError:
51
+ return header_value
52
+
53
+ def _request_json(
54
+ self,
55
+ method: str,
56
+ path: str,
57
+ params: Optional[Dict[str, Any]] = None,
58
+ json_body: Optional[Dict[str, Any]] = None,
59
+ extra_headers: Optional[Dict[str, str]] = None,
60
+ ) -> Dict[str, Any]:
61
+ params = params or {}
62
+ headers = self._headers()
63
+ if extra_headers:
64
+ headers.update(extra_headers)
65
+ attempt = 0
66
+ while True:
67
+ with httpx.Client(timeout=self.timeout) as client:
68
+ response = client.request(
69
+ method.upper(),
70
+ f"{self.base_url}{path}",
71
+ params=params,
72
+ json=json_body,
73
+ headers=headers,
74
+ )
75
+ if response.status_code < 400:
76
+ try:
77
+ payload = response.json()
78
+ except ValueError as exc:
79
+ raise ShipInfoDecodeError("invalid json response") from exc
80
+ if not isinstance(payload, dict):
81
+ raise ShipInfoDecodeError("json payload is not an object")
82
+ return payload
83
+
84
+ retryable = response.status_code in RETRYABLE_STATUS
85
+ if retryable and attempt < self.max_retries:
86
+ attempt += 1
87
+ time.sleep(self.retry_base_seconds * attempt)
88
+ continue
89
+
90
+ response_headers = {k.lower(): v for k, v in response.headers.items()}
91
+ payment_required = self._parse_payment_required(response_headers.get("payment-required"))
92
+ raise ShipInfoHttpError(
93
+ response.status_code,
94
+ response.text,
95
+ retryable,
96
+ response_headers=response_headers,
97
+ payment_required=payment_required,
98
+ )
99
+
100
+ def get_paginated(
101
+ self,
102
+ path: str,
103
+ params: Optional[Dict[str, Any]] = None,
104
+ limit_pages: int = 10,
105
+ cursor_field: str = "next_cursor",
106
+ items_field: Optional[str] = None,
107
+ ) -> Dict[str, Any]:
108
+ params = dict(params or {})
109
+ pages: List[Dict[str, Any]] = []
110
+ all_items: List[Any] = []
111
+
112
+ for _ in range(max(1, limit_pages)):
113
+ payload = self._request_json("GET", path, params=params)
114
+ pages.append(payload)
115
+
116
+ data = payload.get("data") if isinstance(payload, dict) else None
117
+ if isinstance(data, dict):
118
+ if items_field and isinstance(data.get(items_field), list):
119
+ all_items.extend(data[items_field])
120
+ cursor = data.get(cursor_field)
121
+ if not cursor:
122
+ break
123
+ params["cursor"] = cursor
124
+ else:
125
+ break
126
+
127
+ return {"pages": pages, "all_items": all_items}
128
+
129
+ def capabilities(self) -> Dict[str, Any]:
130
+ return self._request_json("GET", "/v1/capabilities")
131
+
132
+ def policy(self) -> Dict[str, Any]:
133
+ return self._request_json("GET", "/v1/policy")
134
+
135
+ def quality(self) -> Dict[str, Any]:
136
+ return self._request_json("GET", "/v1/quality")
137
+
138
+ def billing_pricing(self) -> Dict[str, Any]:
139
+ return self._request_json("GET", "/v1/billing/pricing")
140
+
141
+ def billing_x402_requirements(self, resource: str = "/topos/api/v1/vessels/lookup") -> Dict[str, Any]:
142
+ return self._request_json("GET", "/v1/billing/x402/requirements", params={"resource": resource})
143
+
144
+ def billing_x402_verify(
145
+ self,
146
+ resource: str,
147
+ payment: Dict[str, Any],
148
+ payment_signature: Optional[str] = None,
149
+ ) -> Dict[str, Any]:
150
+ headers: Dict[str, str] = {}
151
+ if payment_signature:
152
+ headers["PAYMENT-SIGNATURE"] = payment_signature
153
+ return self._request_json(
154
+ "POST",
155
+ "/v1/billing/x402/verify",
156
+ json_body={"resource": resource, "payment": payment},
157
+ extra_headers=headers,
158
+ )
159
+
160
+ def vessel_lookup(self, vessel_id: str) -> Dict[str, Any]:
161
+ return self._request_json("GET", "/v1/vessels/lookup", params={"id": vessel_id})
162
+
163
+ def port_congestion(self, port_id: int, range: Optional[str] = None, vessel_type: Optional[str] = None) -> Dict[str, Any]:
164
+ params: Dict[str, Any] = {}
165
+ if range:
166
+ params["range"] = range
167
+ if vessel_type:
168
+ params["vessel_type"] = vessel_type
169
+ return self._request_json("GET", f"/v1/ports/{port_id}/congestion", params=params)
170
+
171
+ def sts_events(self, **kwargs: Any) -> Dict[str, Any]:
172
+ return self._request_json("GET", "/v1/sts/events", params=kwargs)
173
+
174
+ def route_stress_index(self, zone_key: Optional[str] = None, range: Optional[str] = None) -> Dict[str, Any]:
175
+ params: Dict[str, Any] = {}
176
+ if zone_key:
177
+ params["zone_key"] = zone_key
178
+ if range:
179
+ params["range"] = range
180
+ return self._request_json("GET", "/v1/metrics/route_stress_index", params=params)
@@ -0,0 +1,26 @@
1
+ from typing import Any, Dict, Optional
2
+
3
+
4
+ class ShipInfoError(Exception):
5
+ pass
6
+
7
+
8
+ class ShipInfoHttpError(ShipInfoError):
9
+ def __init__(
10
+ self,
11
+ status_code: int,
12
+ body_text: str,
13
+ retryable: bool,
14
+ response_headers: Optional[Dict[str, str]] = None,
15
+ payment_required: Optional[Any] = None,
16
+ ) -> None:
17
+ self.status_code = status_code
18
+ self.body_text = body_text
19
+ self.retryable = retryable
20
+ self.response_headers = response_headers or {}
21
+ self.payment_required = payment_required
22
+ super().__init__(f"HTTP {status_code}: {body_text}")
23
+
24
+
25
+ class ShipInfoDecodeError(ShipInfoError):
26
+ pass
@@ -0,0 +1,68 @@
1
+ Metadata-Version: 2.1
2
+ Name: shipinfo-sdk
3
+ Version: 0.1.1
4
+ Summary: ShipInfo maritime analytics SDK for AI agents
5
+ Project-URL: Homepage, https://shipinfo.net/topos/for-agents
6
+ Project-URL: Repository, https://github.com/bifurcafe/shipinfo-topos
7
+ Project-URL: Issues, https://github.com/bifurcafe/shipinfo-topos/issues
8
+ Keywords: maritime,shipping,ais,vessel-tracking,port-congestion,sts,route-stress,agent-native,ai-agent-api,x402,pay-what-you-want
9
+ Requires-Python: >=3.9
10
+ Description-Content-Type: text/markdown
11
+
12
+ # shipinfo-sdk
13
+
14
+ Python SDK for ShipInfo agent-native API.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ pip install shipinfo-sdk
20
+ ```
21
+
22
+ ## Quickstart
23
+
24
+ ```python
25
+ from shipinfo_sdk import ShipInfoClient
26
+
27
+ client = ShipInfoClient(
28
+ base_url="https://shipinfo.net/topos/api",
29
+ api_key=None,
30
+ agent_headers={
31
+ "name": "demo-agent",
32
+ "vendor": "example-inc",
33
+ "contact": "https://example.com/agent",
34
+ "session": "sess-001",
35
+ },
36
+ )
37
+
38
+ caps = client.capabilities()
39
+ print(caps.get("status"))
40
+ ```
41
+
42
+ ## API
43
+
44
+ - `capabilities()`
45
+ - `policy()`
46
+ - `quality()`
47
+ - `billing_pricing()`
48
+ - `billing_x402_requirements(resource="/topos/api/v1/vessels/lookup")`
49
+ - `billing_x402_verify(resource, payment, payment_signature=None)`
50
+ - `vessel_lookup(vessel_id)`
51
+ - `port_congestion(port_id, range=None, vessel_type=None)`
52
+ - `sts_events(**kwargs)`
53
+ - `route_stress_index(zone_key=None, range=None)`
54
+ - `get_paginated(path, params=None, limit_pages=10, cursor_field="next_cursor", items_field=None)`
55
+
56
+ ## x402 Notes
57
+
58
+ - On HTTP `402`, `ShipInfoHttpError` includes:
59
+ - `status_code`
60
+ - `retryable`
61
+ - `response_headers`
62
+ - `payment_required` (parsed from `PAYMENT-REQUIRED`/`payment-required` header if available)
63
+ - For proof-based verify flow use `payment_signature`; SDK sends `PAYMENT-SIGNATURE` header.
64
+
65
+ ## Fair Value Rule
66
+
67
+ Paid calls follow payer autonomy semantics:
68
+ `agent_self_assessed_fair_value`, `paid_interactions_only`.
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ shipinfo_sdk/__init__.py
4
+ shipinfo_sdk/client.py
5
+ shipinfo_sdk/errors.py
6
+ shipinfo_sdk.egg-info/PKG-INFO
7
+ shipinfo_sdk.egg-info/SOURCES.txt
8
+ shipinfo_sdk.egg-info/dependency_links.txt
9
+ shipinfo_sdk.egg-info/requires.txt
10
+ shipinfo_sdk.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ httpx>=0.27.0
@@ -0,0 +1,2 @@
1
+ dist
2
+ shipinfo_sdk