snowleopard 0.2.0__py3-none-any.whl → 0.3.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.
snowleopard/__init__.py CHANGED
@@ -5,7 +5,7 @@
5
5
  from snowleopard.async_client import AsyncSnowLeopardClient
6
6
  from snowleopard.client import SnowLeopardClient
7
7
 
8
- __version__ = "0.2.0"
8
+ __version__ = "0.3.0"
9
9
 
10
10
  __all__ = [
11
11
  "SnowLeopardClient",
@@ -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.models import ResponseDataObjects, RetrieveResponseObjects, parse
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.raise_for_status()
53
+ if resp.status_code not in (400,):
54
+ resp.raise_for_status()
53
55
  async for line in resp.aiter_lines():
54
- yield parse(json.loads(line))
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__()
snowleopard/cli.py CHANGED
@@ -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.models import RetrieveResponseError
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
- if isinstance(resp, RetrieveResponseError):
95
- sys.exit(1)
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
 
snowleopard/client.py CHANGED
@@ -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.models import parse, RetrieveResponseObjects, ResponseDataObjects
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.raise_for_status()
53
+ if resp.status_code not in (400,):
54
+ resp.raise_for_status()
55
55
  for line in resp.iter_lines():
56
- yield parse(json.loads(line))
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.models import parse
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
- return parse(resp.json())
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
snowleopard/error.py ADDED
@@ -0,0 +1,10 @@
1
+ # -*- coding: utf-8 -*-
2
+ # copyright 2026 Snow Leopard, Inc
3
+ # released under the MIT license - see LICENSE file
4
+
5
+ class SLException(Exception):
6
+ pass
7
+
8
+
9
+ class APIBadRequest(SLException):
10
+ pass
snowleopard/models.py CHANGED
@@ -16,21 +16,21 @@ class StrEnum(str, Enum):
16
16
 
17
17
 
18
18
  @dataclass
19
- class RetrieveResponse:
20
- objType = "retrieveResponse"
19
+ class APIError:
20
+ objType = "apiError"
21
21
 
22
22
  callId: str
23
- data: List[Union[SchemaData, ErrorSchemaData]]
24
- responseStatus: ResponseStatus
23
+ responseStatus: str
24
+ description: str
25
25
 
26
26
 
27
27
  @dataclass
28
- class RetrieveResponseError:
29
- objType = "apiError"
28
+ class RetrieveResponse:
29
+ objType = "retrieveResponse"
30
30
 
31
31
  callId: str
32
- responseStatus: str
33
- description: str
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
- RetrieveResponseObjects = Union[RetrieveResponse, RetrieveResponseError]
126
+
127
+ RetrieveResponseObjects = Union[APIError, RetrieveResponse]
128
+
123
129
 
124
130
  ResponseDataObjects = Union[
125
- ErrorSchemaData,
131
+ APIError,
126
132
  ResponseStart,
127
133
  ResponseData,
128
134
  EarlyTermination,
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: snowleopard
3
- Version: 0.2.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 AI
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
@@ -0,0 +1,12 @@
1
+ snowleopard/__init__.py,sha256=I_L5qQ9a4I0Rj2X-kXrOfz5txJHcwOTJ5k7ByNX908g,314
2
+ snowleopard/async_client.py,sha256=chOaSMgBxYbZ0TF1lG9HbUBncw2gb7hadVT9erC2LxA,2280
3
+ snowleopard/cli.py,sha256=g6fxlrLRp0uTyMJngBuk6ViC18q1SRxizLU8yIV9sKM,4219
4
+ snowleopard/client.py,sha256=L0Q1IPFWeRPKepM_Mrw5CvCUCmtpXEP95TH6UAjWYvA,2193
5
+ snowleopard/client_base.py,sha256=Q1yDASR4Pv-rf3mFUyhMj4KHyG5ja3hi3QZ0io_Xu5M,3912
6
+ snowleopard/error.py,sha256=5OxAStCont28AgFiyTatV9XNjZmgzp-x1A8sPM_TNAQ,196
7
+ snowleopard/models.py,sha256=qOZz3JYUjV8rUnqvzsOkTwbpOVIA46n7-dt4ISY5WM4,2961
8
+ snowleopard-0.3.0.dist-info/METADATA,sha256=dmrfW4qrpAwpHdoxsx-6NsEP3U_sUkmrmyh8KjSwx3c,4225
9
+ snowleopard-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ snowleopard-0.3.0.dist-info/entry_points.txt,sha256=KoByMlsBKmDB6_iwkrTCFyl2BT9bjBuKUZth40aXx70,47
11
+ snowleopard-0.3.0.dist-info/licenses/LICENSE,sha256=Oj4qP5p_cwac6hT-mt861WsDowqGkqIh0w4FMHik38U,1074
12
+ snowleopard-0.3.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.28.0
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Snow Leopard AI
3
+ Copyright (c) 2025 Snow Leopard, Inc
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,11 +0,0 @@
1
- snowleopard/__init__.py,sha256=q2PBrFC6njiGSnk2apaohSpWbvTFFoiV5Aup9D3u3_U,314
2
- snowleopard/async_client.py,sha256=IqWpv5CCYwGtuWAGMHFE4dInP6l643WePTEWvytSCsg,1994
3
- snowleopard/cli.py,sha256=0fXljUETy2RhC2kfuvhk-DKORvWYz0Zm_AUWuqrf1EY,3968
4
- snowleopard/client.py,sha256=YnnCkAdPzumckxhm_DiL2uVsUP9RF_P8ILMZDUF-tlY,1990
5
- snowleopard/client_base.py,sha256=hLmtd6W_wuHp-oh5dTAzpEyRkniyuyho_qJy4-k6cdE,3468
6
- snowleopard/models.py,sha256=A3dFN4X9h31rK-OObvBYJklCgrOdiLpgWZH0L_liak0,2859
7
- snowleopard-0.2.0.dist-info/METADATA,sha256=rlQZtSsoKk9BtKcTsAAHbIUIZr7h5LWakXyv84sPTBc,4223
8
- snowleopard-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
9
- snowleopard-0.2.0.dist-info/entry_points.txt,sha256=KoByMlsBKmDB6_iwkrTCFyl2BT9bjBuKUZth40aXx70,47
10
- snowleopard-0.2.0.dist-info/licenses/LICENSE,sha256=Lw8Ah-T2MngQlqtV8GCML7qD1OPJ5QnccfJBB_U3f2A,1072
11
- snowleopard-0.2.0.dist-info/RECORD,,