struai 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl
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.
- struai/__init__.py +4 -0
- struai/_base.py +17 -3
- struai/_client.py +2 -1
- struai/_version.py +1 -1
- struai/models/__init__.py +3 -1
- struai/models/entities.py +32 -7
- struai/resources/projects.py +5 -5
- {struai-0.1.0.dist-info → struai-0.2.0.dist-info}/METADATA +4 -1
- struai-0.2.0.dist-info/RECORD +18 -0
- struai-0.1.0.dist-info/RECORD +0 -18
- {struai-0.1.0.dist-info → struai-0.2.0.dist-info}/WHEEL +0 -0
struai/__init__.py
CHANGED
|
@@ -60,6 +60,8 @@ from .models import (
|
|
|
60
60
|
QueryResponse,
|
|
61
61
|
# Entities
|
|
62
62
|
Entity,
|
|
63
|
+
EntityListItem,
|
|
64
|
+
EntityRelation,
|
|
63
65
|
Fact,
|
|
64
66
|
)
|
|
65
67
|
|
|
@@ -104,5 +106,7 @@ __all__ = [
|
|
|
104
106
|
"SearchHit",
|
|
105
107
|
"QueryResponse",
|
|
106
108
|
"Entity",
|
|
109
|
+
"EntityListItem",
|
|
110
|
+
"EntityRelation",
|
|
107
111
|
"Fact",
|
|
108
112
|
]
|
struai/_base.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Base HTTP client with retry logic."""
|
|
2
2
|
import time
|
|
3
3
|
from typing import Any, Dict, Optional, Type, TypeVar, Union
|
|
4
|
+
from urllib.parse import urlparse
|
|
4
5
|
|
|
5
6
|
import httpx
|
|
6
7
|
from pydantic import BaseModel
|
|
@@ -20,11 +21,24 @@ from ._version import __version__
|
|
|
20
21
|
|
|
21
22
|
T = TypeVar("T", bound=BaseModel)
|
|
22
23
|
|
|
23
|
-
DEFAULT_BASE_URL = "https://api.stru.ai
|
|
24
|
+
DEFAULT_BASE_URL = "https://api.stru.ai"
|
|
24
25
|
DEFAULT_TIMEOUT = 60.0
|
|
25
26
|
DEFAULT_MAX_RETRIES = 2
|
|
26
27
|
|
|
27
28
|
|
|
29
|
+
def _normalize_base_url(base_url: str) -> str:
|
|
30
|
+
trimmed = base_url.rstrip("/")
|
|
31
|
+
parsed = urlparse(trimmed)
|
|
32
|
+
if parsed.scheme and parsed.netloc:
|
|
33
|
+
path = parsed.path.rstrip("/")
|
|
34
|
+
if path in ("", "/"):
|
|
35
|
+
return f"{trimmed}/v1"
|
|
36
|
+
return trimmed
|
|
37
|
+
if trimmed.endswith("/v1"):
|
|
38
|
+
return trimmed
|
|
39
|
+
return f"{trimmed}/v1"
|
|
40
|
+
|
|
41
|
+
|
|
28
42
|
class BaseClient:
|
|
29
43
|
"""Base HTTP client with retry logic."""
|
|
30
44
|
|
|
@@ -36,7 +50,7 @@ class BaseClient:
|
|
|
36
50
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
37
51
|
):
|
|
38
52
|
self.api_key = api_key
|
|
39
|
-
self.base_url = base_url
|
|
53
|
+
self.base_url = _normalize_base_url(base_url)
|
|
40
54
|
self.timeout = timeout
|
|
41
55
|
self.max_retries = max_retries
|
|
42
56
|
self._client: Optional[httpx.Client] = None
|
|
@@ -195,7 +209,7 @@ class AsyncBaseClient:
|
|
|
195
209
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
196
210
|
):
|
|
197
211
|
self.api_key = api_key
|
|
198
|
-
self.base_url = base_url
|
|
212
|
+
self.base_url = _normalize_base_url(base_url)
|
|
199
213
|
self.timeout = timeout
|
|
200
214
|
self.max_retries = max_retries
|
|
201
215
|
self._client: Optional[httpx.AsyncClient] = None
|
struai/_client.py
CHANGED
|
@@ -14,7 +14,8 @@ class StruAI(BaseClient):
|
|
|
14
14
|
|
|
15
15
|
Args:
|
|
16
16
|
api_key: Your API key. Falls back to STRUAI_API_KEY env var.
|
|
17
|
-
base_url: API base URL. Defaults to
|
|
17
|
+
base_url: API base URL. Defaults to https://api.stru.ai.
|
|
18
|
+
If no path is provided, /v1 is appended automatically.
|
|
18
19
|
timeout: Request timeout in seconds. Default 60.
|
|
19
20
|
max_retries: Max retry attempts for failed requests. Default 2.
|
|
20
21
|
|
struai/_version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""Version information."""
|
|
2
|
-
__version__ = "0.
|
|
2
|
+
__version__ = "0.2.0"
|
struai/models/__init__.py
CHANGED
|
@@ -10,7 +10,7 @@ from .drawings import (
|
|
|
10
10
|
SectionTag,
|
|
11
11
|
TitleBlock,
|
|
12
12
|
)
|
|
13
|
-
from .entities import Entity, EntityLocation, Fact
|
|
13
|
+
from .entities import Entity, EntityListItem, EntityLocation, EntityRelation, Fact
|
|
14
14
|
from .projects import JobStatus, JobStep, Project, Sheet, SheetResult
|
|
15
15
|
from .search import (
|
|
16
16
|
EntitySummary,
|
|
@@ -55,6 +55,8 @@ __all__ = [
|
|
|
55
55
|
"QuerySource",
|
|
56
56
|
# Entities
|
|
57
57
|
"Entity",
|
|
58
|
+
"EntityListItem",
|
|
58
59
|
"EntityLocation",
|
|
60
|
+
"EntityRelation",
|
|
59
61
|
"Fact",
|
|
60
62
|
]
|
struai/models/entities.py
CHANGED
|
@@ -3,27 +3,53 @@ from typing import List, Optional
|
|
|
3
3
|
|
|
4
4
|
from pydantic import BaseModel
|
|
5
5
|
|
|
6
|
+
from .common import BBox
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
class EntityLocation(BaseModel):
|
|
8
10
|
"""Where an entity appears."""
|
|
9
11
|
|
|
10
12
|
sheet_id: str
|
|
11
13
|
sheet_title: Optional[str] = None
|
|
12
|
-
page: int
|
|
14
|
+
page: Optional[int] = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class EntityRelation(BaseModel):
|
|
18
|
+
"""Relationship entry on an entity detail response."""
|
|
19
|
+
|
|
20
|
+
uuid: str
|
|
21
|
+
type: str
|
|
22
|
+
fact: str
|
|
23
|
+
source_id: Optional[str] = None
|
|
24
|
+
source_label: Optional[str] = None
|
|
25
|
+
target_id: Optional[str] = None
|
|
26
|
+
target_label: Optional[str] = None
|
|
13
27
|
|
|
14
28
|
|
|
15
29
|
class Fact(BaseModel):
|
|
16
|
-
"""Relationship between entities."""
|
|
30
|
+
"""Relationship between entities (list endpoint)."""
|
|
17
31
|
|
|
18
32
|
id: str
|
|
33
|
+
type: str
|
|
19
34
|
fact: str
|
|
20
|
-
edge_type: str
|
|
21
35
|
source_id: str
|
|
22
|
-
source_label: Optional[str] = None
|
|
23
36
|
target_id: str
|
|
37
|
+
source_label: Optional[str] = None
|
|
24
38
|
target_label: Optional[str] = None
|
|
25
39
|
|
|
26
40
|
|
|
41
|
+
class EntityListItem(BaseModel):
|
|
42
|
+
"""Entity summary from list endpoint."""
|
|
43
|
+
|
|
44
|
+
id: str
|
|
45
|
+
type: str
|
|
46
|
+
label: str
|
|
47
|
+
description: Optional[str] = None
|
|
48
|
+
sheet_id: Optional[str] = None
|
|
49
|
+
bbox: Optional[BBox] = None
|
|
50
|
+
attributes: Optional[str] = None
|
|
51
|
+
|
|
52
|
+
|
|
27
53
|
class Entity(BaseModel):
|
|
28
54
|
"""Full entity with relationships."""
|
|
29
55
|
|
|
@@ -31,7 +57,6 @@ class Entity(BaseModel):
|
|
|
31
57
|
type: str
|
|
32
58
|
label: str
|
|
33
59
|
description: Optional[str] = None
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
incoming_facts: List[Fact] = []
|
|
60
|
+
outgoing: List[EntityRelation] = []
|
|
61
|
+
incoming: List[EntityRelation] = []
|
|
37
62
|
locations: List[EntityLocation] = []
|
struai/resources/projects.py
CHANGED
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from typing import TYPE_CHECKING, BinaryIO, Dict, List, Optional, Union
|
|
7
7
|
|
|
8
8
|
from .._exceptions import JobFailedError, TimeoutError
|
|
9
|
-
from ..models.entities import Entity, Fact
|
|
9
|
+
from ..models.entities import Entity, EntityListItem, Fact
|
|
10
10
|
from ..models.projects import JobStatus, Project, Sheet, SheetResult
|
|
11
11
|
from ..models.search import QueryResponse, SearchResponse
|
|
12
12
|
|
|
@@ -266,7 +266,7 @@ class Entities:
|
|
|
266
266
|
sheet_id: Optional[str] = None,
|
|
267
267
|
type: Optional[str] = None,
|
|
268
268
|
limit: int = 100,
|
|
269
|
-
) -> List[
|
|
269
|
+
) -> List[EntityListItem]:
|
|
270
270
|
"""List entities in project."""
|
|
271
271
|
params: Dict[str, Union[str, int]] = {"limit": limit}
|
|
272
272
|
if sheet_id:
|
|
@@ -278,7 +278,7 @@ class Entities:
|
|
|
278
278
|
f"/projects/{self._project_id}/entities",
|
|
279
279
|
params=params,
|
|
280
280
|
)
|
|
281
|
-
return [
|
|
281
|
+
return [EntityListItem.model_validate(e) for e in response["entities"]]
|
|
282
282
|
|
|
283
283
|
def get(self, entity_id: str) -> Entity:
|
|
284
284
|
"""Get entity with all relationships."""
|
|
@@ -300,7 +300,7 @@ class AsyncEntities:
|
|
|
300
300
|
sheet_id: Optional[str] = None,
|
|
301
301
|
type: Optional[str] = None,
|
|
302
302
|
limit: int = 100,
|
|
303
|
-
) -> List[
|
|
303
|
+
) -> List[EntityListItem]:
|
|
304
304
|
"""List entities in project."""
|
|
305
305
|
params: Dict[str, Union[str, int]] = {"limit": limit}
|
|
306
306
|
if sheet_id:
|
|
@@ -312,7 +312,7 @@ class AsyncEntities:
|
|
|
312
312
|
f"/projects/{self._project_id}/entities",
|
|
313
313
|
params=params,
|
|
314
314
|
)
|
|
315
|
-
return [
|
|
315
|
+
return [EntityListItem.model_validate(e) for e in response["entities"]]
|
|
316
316
|
|
|
317
317
|
async def get(self, entity_id: str) -> Entity:
|
|
318
318
|
"""Get entity with all relationships."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: struai
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: StruAI Drawing Analysis SDK - AI-powered construction drawing analysis
|
|
5
5
|
Project-URL: Homepage, https://struai.com
|
|
6
6
|
Project-URL: Documentation, https://docs.struai.com/python
|
|
@@ -46,6 +46,9 @@ pip install struai
|
|
|
46
46
|
from struai import StruAI
|
|
47
47
|
|
|
48
48
|
client = StruAI(api_key="sk-xxx") # or set STRUAI_API_KEY env var
|
|
49
|
+
|
|
50
|
+
# Optional: override base URL (http://localhost:8000 or http://localhost:8000/v1)
|
|
51
|
+
client = StruAI(api_key="sk-xxx", base_url="http://localhost:8000")
|
|
49
52
|
```
|
|
50
53
|
|
|
51
54
|
## Tier 1: Raw Detection ($0.02/page)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
struai/__init__.py,sha256=SmHeLcTTzSUNIYXmiDmE2_LRxZl8fUYQwhHHCvJwoGY,2274
|
|
2
|
+
struai/_base.py,sha256=JiAxWI1PMmIFVr9v_hBABra8m2fRkEvBp7d2Vc-5IAk,11208
|
|
3
|
+
struai/_client.py,sha256=jTRb8UbAwgFyfGoSHfO5_3QMgiHbew-s7n3bbabIZ18,3541
|
|
4
|
+
struai/_exceptions.py,sha256=GK0aVnOdkmcFaO16e0lzj9S2NmnTjQiy5H6nH6yDV24,2021
|
|
5
|
+
struai/_version.py,sha256=b49QzOi1rklaJqTPnxwmLbtWwDKXokP9JbtqeJykzp8,49
|
|
6
|
+
struai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
struai/models/__init__.py,sha256=qA_e6ZGG7Uhq9aLX2LeXlnUuV8VE6ur3OCFOPcj1oco,1220
|
|
8
|
+
struai/models/common.py,sha256=qRehKPxDWPG34_LHhxu7T93Wqz1uRRQzIErQhraYAt4,598
|
|
9
|
+
struai/models/drawings.py,sha256=oTvN0ZID1TdUsR1BkJ-6M87nFbQwsYyePd0xoxKns9I,1634
|
|
10
|
+
struai/models/entities.py,sha256=4MHfhCn4g0n6d-6asASklbcmbJcIPg91rMBc5f56Zm8,1348
|
|
11
|
+
struai/models/projects.py,sha256=_VYinZrwWCmv3SQy7iZFPBtHcGsZe3XxI1i9SCk0EfY,1654
|
|
12
|
+
struai/models/search.py,sha256=UL3RYsAidS1zdV4lKiwNcHcAIjzyi8dgAQodowjzh58,1324
|
|
13
|
+
struai/resources/__init__.py,sha256=_JT_8OrvVcJ4yHmvZnsqYj4ej8pVg5lKSPKIcgQTCZk,183
|
|
14
|
+
struai/resources/drawings.py,sha256=b3C7CHS-p8CpGBtKl_dvLeUFDJ3g8HjlLBA4II0HsT4,3957
|
|
15
|
+
struai/resources/projects.py,sha256=_KRHO6ByBN_W-STlaAhVljLt9F3Yh9xzV5Piahc50FQ,19501
|
|
16
|
+
struai-0.2.0.dist-info/METADATA,sha256=aILI4YDk7L0ZjyjOuA_8mmG0gAXSDV6DO5WEKz0S2iU,4460
|
|
17
|
+
struai-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
18
|
+
struai-0.2.0.dist-info/RECORD,,
|
struai-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
struai/__init__.py,sha256=FsxjwfQOKDXzcnX8TqfWgTOqEbe-rW7zf8hwsqSMq0c,2190
|
|
2
|
-
struai/_base.py,sha256=IPhrlgoqf7RZOh9xy2O6o9qMazEj0-RkI-dXPGzk76U,10795
|
|
3
|
-
struai/_client.py,sha256=AmxmJ0ZSPlYlfqf4WHRaR_PQ1u5m3DCE59pLrf8j-7s,3483
|
|
4
|
-
struai/_exceptions.py,sha256=GK0aVnOdkmcFaO16e0lzj9S2NmnTjQiy5H6nH6yDV24,2021
|
|
5
|
-
struai/_version.py,sha256=PrqPeNLB_vK7o-1a2bBnGr1qOX4JyRedkdthES3yNbw,49
|
|
6
|
-
struai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
struai/models/__init__.py,sha256=ddFoRRUY32xeHnjI0twEIRtXNh1huyWwggE1sY-y2e4,1144
|
|
8
|
-
struai/models/common.py,sha256=qRehKPxDWPG34_LHhxu7T93Wqz1uRRQzIErQhraYAt4,598
|
|
9
|
-
struai/models/drawings.py,sha256=oTvN0ZID1TdUsR1BkJ-6M87nFbQwsYyePd0xoxKns9I,1634
|
|
10
|
-
struai/models/entities.py,sha256=dlQJqnY1GbVZOy74Rtt7J2JGxtUI7pWyifAO3whmNP0,753
|
|
11
|
-
struai/models/projects.py,sha256=_VYinZrwWCmv3SQy7iZFPBtHcGsZe3XxI1i9SCk0EfY,1654
|
|
12
|
-
struai/models/search.py,sha256=UL3RYsAidS1zdV4lKiwNcHcAIjzyi8dgAQodowjzh58,1324
|
|
13
|
-
struai/resources/__init__.py,sha256=_JT_8OrvVcJ4yHmvZnsqYj4ej8pVg5lKSPKIcgQTCZk,183
|
|
14
|
-
struai/resources/drawings.py,sha256=b3C7CHS-p8CpGBtKl_dvLeUFDJ3g8HjlLBA4II0HsT4,3957
|
|
15
|
-
struai/resources/projects.py,sha256=3HMLbZmhcPQq51uTT3Y21Rx48PDKXF_-ESPDJYBd5tw,19453
|
|
16
|
-
struai-0.1.0.dist-info/METADATA,sha256=hVEKAZoNtYqtqz_c95ouj7GOmY7jKPVvrfn2tYe3iS0,4309
|
|
17
|
-
struai-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
18
|
-
struai-0.1.0.dist-info/RECORD,,
|
|
File without changes
|