snowleopard 0.2.0__tar.gz → 0.3.0__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.
- {snowleopard-0.2.0 → snowleopard-0.3.0}/LICENSE +1 -1
- {snowleopard-0.2.0 → snowleopard-0.3.0}/PKG-INFO +2 -2
- {snowleopard-0.2.0 → snowleopard-0.3.0}/src/snowleopard/__init__.py +1 -1
- {snowleopard-0.2.0 → snowleopard-0.3.0}/src/snowleopard/async_client.py +8 -3
- {snowleopard-0.2.0 → snowleopard-0.3.0}/src/snowleopard/cli.py +13 -3
- {snowleopard-0.2.0 → snowleopard-0.3.0}/src/snowleopard/client.py +8 -5
- {snowleopard-0.2.0 → snowleopard-0.3.0}/src/snowleopard/client_base.py +10 -2
- snowleopard-0.3.0/src/snowleopard/error.py +10 -0
- {snowleopard-0.2.0 → snowleopard-0.3.0}/src/snowleopard/models.py +17 -11
- snowleopard-0.2.0/.claude/settings.local.json +0 -17
- {snowleopard-0.2.0 → snowleopard-0.3.0}/.gitignore +0 -0
- {snowleopard-0.2.0 → snowleopard-0.3.0}/CONTRIBUTING.md +0 -0
- {snowleopard-0.2.0 → snowleopard-0.3.0}/README.md +0 -0
- {snowleopard-0.2.0 → snowleopard-0.3.0}/pyproject.toml +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: snowleopard
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: snowleopard.ai client library
|
|
5
5
|
Author-email: Snowy <hello@snowleopard.ai>
|
|
6
6
|
License: MIT License
|
|
7
7
|
|
|
8
|
-
Copyright (c) 2025 Snow Leopard
|
|
8
|
+
Copyright (c) 2025 Snow Leopard, Inc
|
|
9
9
|
|
|
10
10
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
11
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -7,7 +7,8 @@ from typing import AsyncGenerator, Optional, Dict, Any
|
|
|
7
7
|
|
|
8
8
|
import httpx
|
|
9
9
|
from snowleopard.client_base import SLClientBase
|
|
10
|
-
from snowleopard.
|
|
10
|
+
from snowleopard.error import APIBadRequest
|
|
11
|
+
from snowleopard.models import APIError, parse, ResponseDataObjects, RetrieveResponseObjects
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class AsyncSnowLeopardClient(SLClientBase):
|
|
@@ -49,9 +50,13 @@ class AsyncSnowLeopardClient(SLClientBase):
|
|
|
49
50
|
self._build_path(datafile_id, "response"),
|
|
50
51
|
json=self._build_request_body(user_query, known_data),
|
|
51
52
|
) as resp:
|
|
52
|
-
resp.
|
|
53
|
+
if resp.status_code not in (400,):
|
|
54
|
+
resp.raise_for_status()
|
|
53
55
|
async for line in resp.aiter_lines():
|
|
54
|
-
|
|
56
|
+
resultObj = parse(json.loads(line))
|
|
57
|
+
if isinstance(resultObj, APIError) and resp.status_code == 400:
|
|
58
|
+
raise APIBadRequest(resultObj.description)
|
|
59
|
+
yield resultObj
|
|
55
60
|
|
|
56
61
|
async def __aenter__(self):
|
|
57
62
|
await self.client.__aenter__()
|
|
@@ -10,7 +10,7 @@ from typing import List, Optional, Dict, Any
|
|
|
10
10
|
|
|
11
11
|
from httpx import HTTPStatusError
|
|
12
12
|
from snowleopard import __version__, SnowLeopardClient
|
|
13
|
-
from snowleopard.
|
|
13
|
+
from snowleopard.error import APIBadRequest
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def _create_parser() -> argparse.ArgumentParser:
|
|
@@ -82,6 +82,7 @@ def _get_client(parsed_args):
|
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
def _retrieve(parsed_args):
|
|
85
|
+
hadErrors = False
|
|
85
86
|
try:
|
|
86
87
|
known_data = _parse_known_data(parsed_args.knownData)
|
|
87
88
|
with _get_client(parsed_args) as client:
|
|
@@ -91,14 +92,18 @@ def _retrieve(parsed_args):
|
|
|
91
92
|
datafile_id=parsed_args.datafile,
|
|
92
93
|
)
|
|
93
94
|
print(json.dumps(dataclasses.asdict(resp)))
|
|
94
|
-
|
|
95
|
-
|
|
95
|
+
except APIBadRequest as e:
|
|
96
|
+
print(f"error: {str(e)}", file=sys.stderr)
|
|
97
|
+
hadErrors = True
|
|
96
98
|
except HTTPStatusError as e:
|
|
97
99
|
print(str(e), file=sys.stderr)
|
|
100
|
+
hadErrors = True
|
|
101
|
+
if hadErrors:
|
|
98
102
|
sys.exit(1)
|
|
99
103
|
|
|
100
104
|
|
|
101
105
|
def _response(parsed_args):
|
|
106
|
+
hadErrors = False
|
|
102
107
|
try:
|
|
103
108
|
known_data = _parse_known_data(parsed_args.knownData)
|
|
104
109
|
with _get_client(parsed_args) as client:
|
|
@@ -108,8 +113,13 @@ def _response(parsed_args):
|
|
|
108
113
|
datafile_id=parsed_args.datafile,
|
|
109
114
|
):
|
|
110
115
|
print(json.dumps(dataclasses.asdict(chunk)))
|
|
116
|
+
except APIBadRequest as e:
|
|
117
|
+
print(f"error: {str(e)}", file=sys.stderr)
|
|
118
|
+
hadErrors = True
|
|
111
119
|
except HTTPStatusError as e:
|
|
112
120
|
print(str(e), file=sys.stderr)
|
|
121
|
+
hadErrors = True
|
|
122
|
+
if hadErrors:
|
|
113
123
|
sys.exit(1)
|
|
114
124
|
|
|
115
125
|
|
|
@@ -7,7 +7,8 @@ from typing import Optional, Generator, Dict, Any
|
|
|
7
7
|
|
|
8
8
|
import httpx
|
|
9
9
|
from snowleopard.client_base import SLClientBase
|
|
10
|
-
from snowleopard.
|
|
10
|
+
from snowleopard.error import APIBadRequest
|
|
11
|
+
from snowleopard.models import APIError, parse, RetrieveResponseObjects, ResponseDataObjects
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class SnowLeopardClient(SLClientBase):
|
|
@@ -35,8 +36,6 @@ class SnowLeopardClient(SLClientBase):
|
|
|
35
36
|
url=self._build_path(datafile_id, "retrieve"),
|
|
36
37
|
json=self._build_request_body(user_query, known_data),
|
|
37
38
|
)
|
|
38
|
-
if resp.status_code not in (200, 409):
|
|
39
|
-
resp.raise_for_status()
|
|
40
39
|
return self._parse_retrieve(resp)
|
|
41
40
|
|
|
42
41
|
def response(
|
|
@@ -51,9 +50,13 @@ class SnowLeopardClient(SLClientBase):
|
|
|
51
50
|
url=self._build_path(datafile_id, "response"),
|
|
52
51
|
json=self._build_request_body(user_query, known_data),
|
|
53
52
|
) as resp:
|
|
54
|
-
resp.
|
|
53
|
+
if resp.status_code not in (400,):
|
|
54
|
+
resp.raise_for_status()
|
|
55
55
|
for line in resp.iter_lines():
|
|
56
|
-
|
|
56
|
+
resultObj = parse(json.loads(line))
|
|
57
|
+
if isinstance(resultObj, APIError) and resp.status_code == 400:
|
|
58
|
+
raise APIBadRequest(resultObj.description)
|
|
59
|
+
yield resultObj
|
|
57
60
|
|
|
58
61
|
def __enter__(self):
|
|
59
62
|
self.client.__enter__()
|
|
@@ -9,7 +9,8 @@ from typing import Optional, Dict, Any
|
|
|
9
9
|
|
|
10
10
|
import httpx
|
|
11
11
|
|
|
12
|
-
from snowleopard.
|
|
12
|
+
from snowleopard.error import APIBadRequest
|
|
13
|
+
from snowleopard.models import APIError, parse
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
@dataclass
|
|
@@ -94,6 +95,8 @@ class SLClientBase:
|
|
|
94
95
|
def _build_request_body(
|
|
95
96
|
user_query: str, known_data: Optional[Dict[str, Any]] = None
|
|
96
97
|
) -> Dict[str, Any]:
|
|
98
|
+
if not isinstance(user_query, str) or not user_query.strip():
|
|
99
|
+
raise APIBadRequest('userQuery field must not be empty/whitespace')
|
|
97
100
|
body = {"userQuery": user_query}
|
|
98
101
|
if known_data is not None:
|
|
99
102
|
body["knownData"] = known_data
|
|
@@ -101,8 +104,13 @@ class SLClientBase:
|
|
|
101
104
|
|
|
102
105
|
@staticmethod
|
|
103
106
|
def _parse_retrieve(resp):
|
|
107
|
+
if resp.status_code not in (400, 409):
|
|
108
|
+
resp.raise_for_status()
|
|
104
109
|
try:
|
|
105
|
-
|
|
110
|
+
resultObj = parse(resp.json())
|
|
106
111
|
except Exception:
|
|
107
112
|
resp.raise_for_status()
|
|
108
113
|
raise
|
|
114
|
+
if isinstance(resultObj, APIError) and resp.status_code == 400:
|
|
115
|
+
raise APIBadRequest(resultObj.description)
|
|
116
|
+
return resultObj
|
|
@@ -16,21 +16,21 @@ class StrEnum(str, Enum):
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
@dataclass
|
|
19
|
-
class
|
|
20
|
-
objType = "
|
|
19
|
+
class APIError:
|
|
20
|
+
objType = "apiError"
|
|
21
21
|
|
|
22
22
|
callId: str
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
responseStatus: str
|
|
24
|
+
description: str
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
@dataclass
|
|
28
|
-
class
|
|
29
|
-
objType = "
|
|
28
|
+
class RetrieveResponse:
|
|
29
|
+
objType = "retrieveResponse"
|
|
30
30
|
|
|
31
31
|
callId: str
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
data: List[Union[SchemaData, ErrorSchemaData]]
|
|
33
|
+
responseStatus: ResponseStatus
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
@dataclass
|
|
@@ -97,19 +97,23 @@ class ResponseLLMResult:
|
|
|
97
97
|
|
|
98
98
|
class ResponseStatus(StrEnum):
|
|
99
99
|
SUCCESS = "SUCCESS"
|
|
100
|
+
BAD_REQUEST = "BAD_REQUEST"
|
|
100
101
|
NOT_FOUND_IN_SCHEMA = "NOT_FOUND_IN_SCHEMA"
|
|
101
102
|
UNKNOWN = "UNKNOWN"
|
|
102
103
|
INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR"
|
|
103
104
|
AUTHORIZATION_FAILED = "AUTHORIZATION_FAILED"
|
|
104
105
|
LLM_ERROR = "LLM_ERROR"
|
|
105
106
|
LLM_TOKEN_LIMIT_REACHED = "LLM_TOKEN_LIMIT_REACHED"
|
|
107
|
+
DB_ERROR = "DB_ERROR"
|
|
108
|
+
DB_CONNECTION_ERROR = "DB_CONNECTION_ERROR"
|
|
109
|
+
DB_SYNTAX_ERROR = "DB_SYNTAX_ERROR"
|
|
106
110
|
|
|
107
111
|
|
|
108
112
|
_PARSE_OBJS = {
|
|
109
113
|
o.objType: o
|
|
110
114
|
for o in (
|
|
115
|
+
APIError,
|
|
111
116
|
RetrieveResponse,
|
|
112
|
-
RetrieveResponseError,
|
|
113
117
|
SchemaData,
|
|
114
118
|
ErrorSchemaData,
|
|
115
119
|
ResponseStart,
|
|
@@ -119,10 +123,12 @@ _PARSE_OBJS = {
|
|
|
119
123
|
)
|
|
120
124
|
}
|
|
121
125
|
|
|
122
|
-
|
|
126
|
+
|
|
127
|
+
RetrieveResponseObjects = Union[APIError, RetrieveResponse]
|
|
128
|
+
|
|
123
129
|
|
|
124
130
|
ResponseDataObjects = Union[
|
|
125
|
-
|
|
131
|
+
APIError,
|
|
126
132
|
ResponseStart,
|
|
127
133
|
ResponseData,
|
|
128
134
|
EarlyTermination,
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(mkdir:*)",
|
|
5
|
-
"Bash(uv add:*)",
|
|
6
|
-
"Bash(uv run pytest:*)",
|
|
7
|
-
"WebFetch(domain:www.python-httpx.org)",
|
|
8
|
-
"Bash(python -c:*)",
|
|
9
|
-
"Bash(uv run python:*)",
|
|
10
|
-
"WebFetch(domain:github.com)",
|
|
11
|
-
"Bash(uv run snowy response --help:*)",
|
|
12
|
-
"Bash(uv run:*)"
|
|
13
|
-
],
|
|
14
|
-
"deny": [],
|
|
15
|
-
"ask": []
|
|
16
|
-
}
|
|
17
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|