snowleopard 0.1.1__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.
@@ -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 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: snowleopard
3
- Version: 0.1.1
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
@@ -34,7 +34,9 @@ Description-Content-Type: text/markdown
34
34
 
35
35
  # Snow Leopard SDK for Python
36
36
 
37
- Python client library for [Snow Leopard Playground](https://try.snowleopard.ai) APIs.
37
+ This repo contains the Python client library for [Snow Leopard Playground](https://try.snowleopard.ai) APIs.
38
+
39
+ See our [API documentation](https://docs.snowleopard.ai/#api-documentation) for more details.
38
40
 
39
41
  ## Installation
40
42
 
@@ -45,16 +47,13 @@ pip install snowleopard
45
47
  ## Quick Start
46
48
 
47
49
  ```python
48
- from snowleopard import SnowLeopardPlaygroundClient
50
+ from snowleopard import SnowLeopardClient
49
51
 
50
- # Initialize the client (or AsyncSnowLeopardPlaygroundClient)
51
- client = SnowLeopardPlaygroundClient(api_key="your-api-key")
52
+ # Initialize the client (or AsyncSnowLeopardClient)
53
+ client = SnowLeopardClient(api_key="your-api-key")
52
54
 
53
55
  # Query your data in natural language
54
- response = client.retrieve(
55
- datafile_id="your-datafile-id",
56
- user_query="How many users signed up last month?"
57
- )
56
+ response = client.retrieve(user_query="How many users signed up last month?", datafile_id="your-datafile-id")
58
57
  ```
59
58
 
60
59
  ## Getting Started
@@ -69,7 +68,7 @@ response = client.retrieve(
69
68
  Or pass it directly to the client:
70
69
 
71
70
  ```python
72
- SnowLeopardPlaygroundClient(api_key="your-api-key")
71
+ SnowLeopardClient(api_key="your-api-key")
73
72
  ```
74
73
 
75
74
  ## Usage
@@ -77,31 +76,31 @@ response = client.retrieve(
77
76
  ### Synchronous Client
78
77
 
79
78
  ```python
80
- from snowleopard import SnowLeopardPlaygroundClient
79
+ from snowleopard import SnowLeopardClient
81
80
 
82
- with SnowLeopardPlaygroundClient() as client:
83
- # Get data directly from a natural language query
84
- response = client.retrieve("datafile-id", "What's the total revenue?")
85
- print(response.data)
86
-
87
- # Stream natural language summary of live data
88
- for chunk in client.response("datafile-id", "Show me top 10 customers"):
89
- print(chunk)
81
+ with SnowLeopardClient() as client:
82
+ # Get data directly from a natural language query
83
+ response = client.retrieve(user_query="How many superheroes are there?")
84
+ print(response.data)
85
+
86
+ # Stream natural language summary of live data
87
+ for chunk in client.response(user_query="How many superheroes are there?"):
88
+ print(chunk)
90
89
  ```
91
90
 
92
91
  ### Async Client
93
92
 
94
93
  ```python
95
- from snowleopard import AsyncSnowLeopardPlaygroundClient
94
+ from snowleopard import AsyncSnowLeopardClient
96
95
 
97
- async with AsyncSnowLeopardPlaygroundClient() as client:
98
- # Get complete results
99
- response = await client.retrieve("datafile-id", "What's the total revenue?")
100
- print(response.data)
96
+ async with AsyncSnowLeopardClient() as client:
97
+ # Get complete results
98
+ response = await client.retrieve(user_query="How many superheroes are there?")
99
+ print(response.data)
101
100
 
102
- # Get streaming results
103
- async for chunk in client.response("datafile-id", "Show me top 10 customers"):
104
- print(chunk)
101
+ # Get streaming results
102
+ async for chunk in client.response(user_query="How many superheroes are there?"):
103
+ print(chunk)
105
104
  ```
106
105
 
107
106
  ### CLI
@@ -110,10 +109,22 @@ The SDK includes a command-line interface:
110
109
 
111
110
  ```bash
112
111
  pip install snowleopard
113
- snowy retrieve <datafile-id> "How many records are there?"
114
- snowy response <datafile-id> "Summarize the data"
112
+ snowy retrieve --datafile <datafile-id> "How many records are there?"
113
+ snowy response --datafile <datafile-id> "Summarize the data"
115
114
  ```
116
115
 
116
+ ### On Premise Customers
117
+
118
+ For our customers who have a separate deployment per dataset, you should declare <url> explicitly when creating a
119
+ client and omit <datafile id> when querying.
120
+
121
+ Example:
122
+ ```python
123
+ client = SnowLeopardClient(url="https://{your-vm-ip}:{port}", api_key="your-api-key")
124
+ response = client.retrieve(user_query="How many users signed up last month?")
125
+ ```
126
+
127
+
117
128
  ## Contributing
118
129
 
119
130
  For SDK developer docs and how to contribute, see [CONTRIBUTING.md](CONTRIBUTING.md)
@@ -0,0 +1,96 @@
1
+ # Snow Leopard SDK for Python
2
+
3
+ This repo contains the Python client library for [Snow Leopard Playground](https://try.snowleopard.ai) APIs.
4
+
5
+ See our [API documentation](https://docs.snowleopard.ai/#api-documentation) for more details.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install snowleopard
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```python
16
+ from snowleopard import SnowLeopardClient
17
+
18
+ # Initialize the client (or AsyncSnowLeopardClient)
19
+ client = SnowLeopardClient(api_key="your-api-key")
20
+
21
+ # Query your data in natural language
22
+ response = client.retrieve(user_query="How many users signed up last month?", datafile_id="your-datafile-id")
23
+ ```
24
+
25
+ ## Getting Started
26
+
27
+ 1. **Get your API key** from [https://auth.snowleopard.ai/account/api_keys](https://auth.snowleopard.ai/account/api_keys)
28
+ 2. **Upload your datafiles** at [https://try.snowleopard.ai](https://try.snowleopard.ai)
29
+ 3. **Set your API key** via environment variable:
30
+ ```bash
31
+ export SNOWLEOPARD_API_KEY="your-api-key"
32
+ ```
33
+
34
+ Or pass it directly to the client:
35
+
36
+ ```python
37
+ SnowLeopardClient(api_key="your-api-key")
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ### Synchronous Client
43
+
44
+ ```python
45
+ from snowleopard import SnowLeopardClient
46
+
47
+ with SnowLeopardClient() as client:
48
+ # Get data directly from a natural language query
49
+ response = client.retrieve(user_query="How many superheroes are there?")
50
+ print(response.data)
51
+
52
+ # Stream natural language summary of live data
53
+ for chunk in client.response(user_query="How many superheroes are there?"):
54
+ print(chunk)
55
+ ```
56
+
57
+ ### Async Client
58
+
59
+ ```python
60
+ from snowleopard import AsyncSnowLeopardClient
61
+
62
+ async with AsyncSnowLeopardClient() as client:
63
+ # Get complete results
64
+ response = await client.retrieve(user_query="How many superheroes are there?")
65
+ print(response.data)
66
+
67
+ # Get streaming results
68
+ async for chunk in client.response(user_query="How many superheroes are there?"):
69
+ print(chunk)
70
+ ```
71
+
72
+ ### CLI
73
+
74
+ The SDK includes a command-line interface:
75
+
76
+ ```bash
77
+ pip install snowleopard
78
+ snowy retrieve --datafile <datafile-id> "How many records are there?"
79
+ snowy response --datafile <datafile-id> "Summarize the data"
80
+ ```
81
+
82
+ ### On Premise Customers
83
+
84
+ For our customers who have a separate deployment per dataset, you should declare <url> explicitly when creating a
85
+ client and omit <datafile id> when querying.
86
+
87
+ Example:
88
+ ```python
89
+ client = SnowLeopardClient(url="https://{your-vm-ip}:{port}", api_key="your-api-key")
90
+ response = client.retrieve(user_query="How many users signed up last month?")
91
+ ```
92
+
93
+
94
+ ## Contributing
95
+
96
+ For SDK developer docs and how to contribute, see [CONTRIBUTING.md](CONTRIBUTING.md)
@@ -0,0 +1,13 @@
1
+ # -*- coding: utf-8 -*-
2
+ # copyright 2025 Snow Leopard, Inc
3
+ # released under the MIT license - see LICENSE file
4
+
5
+ from snowleopard.async_client import AsyncSnowLeopardClient
6
+ from snowleopard.client import SnowLeopardClient
7
+
8
+ __version__ = "0.3.0"
9
+
10
+ __all__ = [
11
+ "SnowLeopardClient",
12
+ "AsyncSnowLeopardClient",
13
+ ]
@@ -7,10 +7,11 @@ 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
- class AsyncSnowLeopardPlaygroundClient(SLClientBase):
14
+ class AsyncSnowLeopardClient(SLClientBase):
14
15
  client: httpx.AsyncClient
15
16
 
16
17
  def __init__(
@@ -26,9 +27,10 @@ class AsyncSnowLeopardPlaygroundClient(SLClientBase):
26
27
 
27
28
  async def retrieve(
28
29
  self,
29
- datafile_id: str,
30
+ *,
30
31
  user_query: str,
31
32
  known_data: Optional[Dict[str, Any]] = None,
33
+ datafile_id: Optional[str] = None,
32
34
  ) -> RetrieveResponseObjects:
33
35
  resp = await self.client.post(
34
36
  url=self._build_path(datafile_id, "retrieve"),
@@ -38,18 +40,23 @@ class AsyncSnowLeopardPlaygroundClient(SLClientBase):
38
40
 
39
41
  async def response(
40
42
  self,
41
- datafile_id: str,
42
- user_query: str,
43
+ *,
43
44
  known_data: Optional[Dict[str, Any]] = None,
45
+ user_query: str,
46
+ datafile_id: Optional[str] = None,
44
47
  ) -> AsyncGenerator[ResponseDataObjects, None]:
45
48
  async with self.client.stream(
46
49
  "POST",
47
50
  self._build_path(datafile_id, "response"),
48
51
  json=self._build_request_body(user_query, known_data),
49
52
  ) as resp:
50
- resp.raise_for_status()
53
+ if resp.status_code not in (400,):
54
+ resp.raise_for_status()
51
55
  async for line in resp.aiter_lines():
52
- 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
53
60
 
54
61
  async def __aenter__(self):
55
62
  await self.client.__aenter__()
@@ -9,8 +9,8 @@ import argparse
9
9
  from typing import List, Optional, Dict, Any
10
10
 
11
11
  from httpx import HTTPStatusError
12
- from snowleopard import __version__, SnowLeopardPlaygroundClient
13
- from snowleopard.models import RetrieveResponseError
12
+ from snowleopard import __version__, SnowLeopardClient
13
+ from snowleopard.error import APIBadRequest
14
14
 
15
15
 
16
16
  def _create_parser() -> argparse.ArgumentParser:
@@ -41,13 +41,15 @@ def _create_parser() -> argparse.ArgumentParser:
41
41
  response.set_defaults(command_func=_response)
42
42
 
43
43
  for subparser in (retrieve, response):
44
+ subparser.add_argument(
45
+ "--datafile", "-df", type=str, help="ID for playground datafile to query"
46
+ )
44
47
  subparser.add_argument(
45
48
  "--knownData",
46
49
  "-d",
47
50
  action="append",
48
51
  help="Known data in key=value format (can be specified multiple times)",
49
52
  )
50
- subparser.add_argument("datafile", type=str, help="ID for Datafile to query")
51
53
  subparser.add_argument("question", type=str, help="Natural language query")
52
54
 
53
55
  return parser
@@ -60,7 +62,10 @@ def _parse_known_data(known_data_list: Optional[List[str]]) -> Optional[Dict[str
60
62
  result = {}
61
63
  for item in known_data_list:
62
64
  if "=" not in item:
63
- print(f"Error: Invalid knownData format '{item}'. Expected key=value", file=sys.stderr)
65
+ print(
66
+ f"Error: Invalid knownData format '{item}'. Expected key=value",
67
+ file=sys.stderr,
68
+ )
64
69
  sys.exit(1)
65
70
  key, value = item.split("=", 1)
66
71
  result[key] = value
@@ -69,7 +74,7 @@ def _parse_known_data(known_data_list: Optional[List[str]]) -> Optional[Dict[str
69
74
 
70
75
  def _get_client(parsed_args):
71
76
  try:
72
- client = SnowLeopardPlaygroundClient(api_key=parsed_args.apikey, loc=parsed_args.loc)
77
+ client = SnowLeopardClient(api_key=parsed_args.apikey, loc=parsed_args.loc)
73
78
  except Exception as e:
74
79
  print(str(e), file=sys.stderr)
75
80
  sys.exit(1)
@@ -77,26 +82,44 @@ def _get_client(parsed_args):
77
82
 
78
83
 
79
84
  def _retrieve(parsed_args):
85
+ hadErrors = False
80
86
  try:
81
87
  known_data = _parse_known_data(parsed_args.knownData)
82
88
  with _get_client(parsed_args) as client:
83
- resp = client.retrieve(parsed_args.datafile, parsed_args.question, known_data)
89
+ resp = client.retrieve(
90
+ user_query=parsed_args.question,
91
+ known_data=known_data,
92
+ datafile_id=parsed_args.datafile,
93
+ )
84
94
  print(json.dumps(dataclasses.asdict(resp)))
85
- if isinstance(resp, RetrieveResponseError):
86
- sys.exit(1)
95
+ except APIBadRequest as e:
96
+ print(f"error: {str(e)}", file=sys.stderr)
97
+ hadErrors = True
87
98
  except HTTPStatusError as e:
88
99
  print(str(e), file=sys.stderr)
100
+ hadErrors = True
101
+ if hadErrors:
89
102
  sys.exit(1)
90
103
 
91
104
 
92
105
  def _response(parsed_args):
106
+ hadErrors = False
93
107
  try:
94
108
  known_data = _parse_known_data(parsed_args.knownData)
95
109
  with _get_client(parsed_args) as client:
96
- for chunk in client.response(parsed_args.datafile, parsed_args.question, known_data):
110
+ for chunk in client.response(
111
+ known_data=known_data,
112
+ user_query=parsed_args.question,
113
+ datafile_id=parsed_args.datafile,
114
+ ):
97
115
  print(json.dumps(dataclasses.asdict(chunk)))
116
+ except APIBadRequest as e:
117
+ print(f"error: {str(e)}", file=sys.stderr)
118
+ hadErrors = True
98
119
  except HTTPStatusError as e:
99
120
  print(str(e), file=sys.stderr)
121
+ hadErrors = True
122
+ if hadErrors:
100
123
  sys.exit(1)
101
124
 
102
125
 
@@ -7,10 +7,11 @@ 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
- class SnowLeopardPlaygroundClient(SLClientBase):
14
+ class SnowLeopardClient(SLClientBase):
14
15
  client: httpx.Client
15
16
 
16
17
  def __init__(
@@ -26,32 +27,36 @@ class SnowLeopardPlaygroundClient(SLClientBase):
26
27
 
27
28
  def retrieve(
28
29
  self,
29
- datafile_id: str,
30
+ *,
30
31
  user_query: str,
31
32
  known_data: Optional[Dict[str, Any]] = None,
33
+ datafile_id: Optional[str] = None,
32
34
  ) -> RetrieveResponseObjects:
33
35
  resp = self.client.post(
34
36
  url=self._build_path(datafile_id, "retrieve"),
35
37
  json=self._build_request_body(user_query, known_data),
36
38
  )
37
- if resp.status_code not in (200, 409):
38
- resp.raise_for_status()
39
39
  return self._parse_retrieve(resp)
40
40
 
41
41
  def response(
42
42
  self,
43
- datafile_id: str,
44
- user_query: str,
43
+ *,
45
44
  known_data: Optional[Dict[str, Any]] = None,
45
+ user_query: str,
46
+ datafile_id: Optional[str] = None,
46
47
  ) -> Generator[ResponseDataObjects, None, None]:
47
48
  with self.client.stream(
48
49
  "POST",
49
50
  url=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
  for line in resp.iter_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
  def __enter__(self):
57
62
  self.client.__enter__()
@@ -0,0 +1,116 @@
1
+ # -*- coding: utf-8 -*-
2
+ # copyright 2025 Snow Leopard, Inc
3
+ # released under the MIT license - see LICENSE file
4
+
5
+ import os
6
+ from abc import abstractmethod
7
+ from dataclasses import dataclass
8
+ from typing import Optional, Dict, Any
9
+
10
+ import httpx
11
+
12
+ from snowleopard.error import APIBadRequest
13
+ from snowleopard.models import APIError, parse
14
+
15
+
16
+ @dataclass
17
+ class SLConfig:
18
+ api_key: str
19
+ timeout: httpx.Timeout
20
+ loc: str
21
+
22
+ def headers(self):
23
+ headers = {}
24
+ if self.api_key:
25
+ headers["Authorization"] = f"Bearer {self.api_key}"
26
+ return headers
27
+
28
+
29
+ class SLClientBase:
30
+ @abstractmethod
31
+ def retrieve(
32
+ self,
33
+ *,
34
+ user_query: str,
35
+ known_data: Optional[Dict[str, Any]] = None,
36
+ datafile_id: Optional[str] = None,
37
+ ):
38
+ """
39
+ The primary for developers building AI agents that needs to retrieve data from a database directly.
40
+
41
+ Takes a natural language question (usually from the user or the agent) and returns the data required to answer
42
+ the query in an LLM-friendly object.
43
+
44
+ :param datafile_id: (optional) The playground datafile-id if hitting a Snow Leopard api directly
45
+ :param user_query: Natural language query to execute against the Playground datafile
46
+ :param known_data: Additional context about the user_query
47
+ """
48
+ ...
49
+
50
+ @abstractmethod
51
+ def response(
52
+ self,
53
+ *,
54
+ known_data: Optional[Dict[str, Any]] = None,
55
+ user_query: str,
56
+ datafile_id: Optional[str] = None,
57
+ ):
58
+ """
59
+ Takes a natural language question (usually from the user or the agent) and returns the data required to answer
60
+ the query as well as a LLM summary of the returned data.
61
+
62
+ :param datafile_id: (optional) The playground datafile-id if hitting a Snow Leopard api directly
63
+ :param user_query: Natural language query to execute against the Playground datafile
64
+ :param known_data: (optional) Additional context about the user_query
65
+ """
66
+ ...
67
+
68
+ @staticmethod
69
+ def _config(
70
+ api_key: Optional[str], timeout: Optional[httpx.Timeout], loc: Optional[str]
71
+ ) -> SLConfig:
72
+ api_key = api_key or os.environ.get("SNOWLEOPARD_API_KEY")
73
+ if api_key is None:
74
+ raise ValueError(
75
+ 'Missing required argument "api_key" and environment variable "SNOWLEOPARD_API_KEY" not set'
76
+ )
77
+ timeout = timeout or httpx.Timeout(
78
+ connect=5.0, read=600.0, write=10.0, pool=5.0
79
+ )
80
+ loc = loc or os.environ.get("SNOWLEOPARD_LOC", "https://api.snowleopard.ai")
81
+ if not loc:
82
+ raise ValueError(
83
+ 'Missing required argument "loc" and environment variable "SNOWLEOPARD_LOC" not set'
84
+ )
85
+ return SLConfig(api_key, timeout, loc)
86
+
87
+ @staticmethod
88
+ def _build_path(datafile_id: str, endpoint: str) -> str:
89
+ if datafile_id is None:
90
+ return endpoint
91
+ else:
92
+ return f"datafiles/{datafile_id}/{endpoint}"
93
+
94
+ @staticmethod
95
+ def _build_request_body(
96
+ user_query: str, known_data: Optional[Dict[str, Any]] = None
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')
100
+ body = {"userQuery": user_query}
101
+ if known_data is not None:
102
+ body["knownData"] = known_data
103
+ return body
104
+
105
+ @staticmethod
106
+ def _parse_retrieve(resp):
107
+ if resp.status_code not in (400, 409):
108
+ resp.raise_for_status()
109
+ try:
110
+ resultObj = parse(resp.json())
111
+ except Exception:
112
+ resp.raise_for_status()
113
+ raise
114
+ if isinstance(resultObj, APIError) and resp.status_code == 400:
115
+ raise APIBadRequest(resultObj.description)
116
+ return resultObj
@@ -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
@@ -11,25 +11,26 @@ from typing import Any, Dict, List, Optional, Union
11
11
 
12
12
  class StrEnum(str, Enum):
13
13
  """String enum compatible with Python 3.8"""
14
+
14
15
  pass
15
16
 
16
17
 
17
18
  @dataclass
18
- class RetrieveResponse:
19
- objType = "retrieveResponse"
19
+ class APIError:
20
+ objType = "apiError"
20
21
 
21
22
  callId: str
22
- data: List[Union[SchemaData, ErrorSchemaData]]
23
- responseStatus: ResponseStatus
23
+ responseStatus: str
24
+ description: str
24
25
 
25
26
 
26
27
  @dataclass
27
- class RetrieveResponseError:
28
- objType = "apiError"
28
+ class RetrieveResponse:
29
+ objType = "retrieveResponse"
29
30
 
30
31
  callId: str
31
- responseStatus: str
32
- description: str
32
+ data: List[Union[SchemaData, ErrorSchemaData]]
33
+ responseStatus: ResponseStatus
33
34
 
34
35
 
35
36
  @dataclass
@@ -96,19 +97,23 @@ class ResponseLLMResult:
96
97
 
97
98
  class ResponseStatus(StrEnum):
98
99
  SUCCESS = "SUCCESS"
100
+ BAD_REQUEST = "BAD_REQUEST"
99
101
  NOT_FOUND_IN_SCHEMA = "NOT_FOUND_IN_SCHEMA"
100
102
  UNKNOWN = "UNKNOWN"
101
103
  INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR"
102
104
  AUTHORIZATION_FAILED = "AUTHORIZATION_FAILED"
103
105
  LLM_ERROR = "LLM_ERROR"
104
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"
105
110
 
106
111
 
107
112
  _PARSE_OBJS = {
108
113
  o.objType: o
109
114
  for o in (
115
+ APIError,
110
116
  RetrieveResponse,
111
- RetrieveResponseError,
112
117
  SchemaData,
113
118
  ErrorSchemaData,
114
119
  ResponseStart,
@@ -118,10 +123,12 @@ _PARSE_OBJS = {
118
123
  )
119
124
  }
120
125
 
121
- RetrieveResponseObjects = Union[RetrieveResponse, RetrieveResponseError]
126
+
127
+ RetrieveResponseObjects = Union[APIError, RetrieveResponse]
128
+
122
129
 
123
130
  ResponseDataObjects = Union[
124
- ErrorSchemaData,
131
+ APIError,
125
132
  ResponseStart,
126
133
  ResponseData,
127
134
  EarlyTermination,
@@ -1,85 +0,0 @@
1
- # Snow Leopard SDK for Python
2
-
3
- Python client library for [Snow Leopard Playground](https://try.snowleopard.ai) APIs.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- pip install snowleopard
9
- ```
10
-
11
- ## Quick Start
12
-
13
- ```python
14
- from snowleopard import SnowLeopardPlaygroundClient
15
-
16
- # Initialize the client (or AsyncSnowLeopardPlaygroundClient)
17
- client = SnowLeopardPlaygroundClient(api_key="your-api-key")
18
-
19
- # Query your data in natural language
20
- response = client.retrieve(
21
- datafile_id="your-datafile-id",
22
- user_query="How many users signed up last month?"
23
- )
24
- ```
25
-
26
- ## Getting Started
27
-
28
- 1. **Get your API key** from [https://auth.snowleopard.ai/account/api_keys](https://auth.snowleopard.ai/account/api_keys)
29
- 2. **Upload your datafiles** at [https://try.snowleopard.ai](https://try.snowleopard.ai)
30
- 3. **Set your API key** via environment variable:
31
- ```bash
32
- export SNOWLEOPARD_API_KEY="your-api-key"
33
- ```
34
-
35
- Or pass it directly to the client:
36
-
37
- ```python
38
- SnowLeopardPlaygroundClient(api_key="your-api-key")
39
- ```
40
-
41
- ## Usage
42
-
43
- ### Synchronous Client
44
-
45
- ```python
46
- from snowleopard import SnowLeopardPlaygroundClient
47
-
48
- with SnowLeopardPlaygroundClient() as client:
49
- # Get data directly from a natural language query
50
- response = client.retrieve("datafile-id", "What's the total revenue?")
51
- print(response.data)
52
-
53
- # Stream natural language summary of live data
54
- for chunk in client.response("datafile-id", "Show me top 10 customers"):
55
- print(chunk)
56
- ```
57
-
58
- ### Async Client
59
-
60
- ```python
61
- from snowleopard import AsyncSnowLeopardPlaygroundClient
62
-
63
- async with AsyncSnowLeopardPlaygroundClient() as client:
64
- # Get complete results
65
- response = await client.retrieve("datafile-id", "What's the total revenue?")
66
- print(response.data)
67
-
68
- # Get streaming results
69
- async for chunk in client.response("datafile-id", "Show me top 10 customers"):
70
- print(chunk)
71
- ```
72
-
73
- ### CLI
74
-
75
- The SDK includes a command-line interface:
76
-
77
- ```bash
78
- pip install snowleopard
79
- snowy retrieve <datafile-id> "How many records are there?"
80
- snowy response <datafile-id> "Summarize the data"
81
- ```
82
-
83
- ## Contributing
84
-
85
- For SDK developer docs and how to contribute, see [CONTRIBUTING.md](CONTRIBUTING.md)
@@ -1,13 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # copyright 2025 Snow Leopard, Inc
3
- # released under the MIT license - see LICENSE file
4
-
5
- from snowleopard.async_client import AsyncSnowLeopardPlaygroundClient
6
- from snowleopard.client import SnowLeopardPlaygroundClient
7
-
8
- __version__ = "0.1.1"
9
-
10
- __all__ = [
11
- "SnowLeopardPlaygroundClient",
12
- "AsyncSnowLeopardPlaygroundClient",
13
- ]
@@ -1,66 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # copyright 2025 Snow Leopard, Inc
3
- # released under the MIT license - see LICENSE file
4
-
5
- import os
6
- from dataclasses import dataclass
7
- from typing import Optional, Dict, Any
8
-
9
- import httpx
10
-
11
- from snowleopard.models import parse
12
-
13
-
14
- @dataclass
15
- class SLConfig:
16
- api_key: str
17
- timeout: httpx.Timeout
18
- loc: str
19
-
20
- def headers(self):
21
- headers = {}
22
- if self.api_key:
23
- headers["Authorization"] = f"Bearer {self.api_key}"
24
- return headers
25
-
26
-
27
- class SLClientBase:
28
- @staticmethod
29
- def _config(
30
- api_key: Optional[str], timeout: Optional[httpx.Timeout], loc: Optional[str]
31
- ) -> SLConfig:
32
- api_key = api_key or os.environ.get("SNOWLEOPARD_API_KEY")
33
- if api_key is None:
34
- raise ValueError(
35
- 'Missing required argument "api_key" and environment variable "SNOWLEOPARD_API_KEY" not set'
36
- )
37
- timeout = timeout or httpx.Timeout(
38
- connect=5.0, read=600.0, write=10.0, pool=5.0
39
- )
40
- loc = loc or os.environ.get("SNOWLEOPARD_LOC", "https://api.snowleopard.ai")
41
- if not loc:
42
- raise ValueError(
43
- 'Missing required argument "loc" and environment variable "SNOWLEOPARD_LOC" not set'
44
- )
45
- return SLConfig(api_key, timeout, loc)
46
-
47
- @staticmethod
48
- def _build_path(datafile_id: str, endpoint: str) -> str:
49
- return f"datafiles/{datafile_id}/{endpoint}"
50
-
51
- @staticmethod
52
- def _build_request_body(
53
- user_query: str, known_data: Optional[Dict[str, Any]] = None
54
- ) -> Dict[str, Any]:
55
- body = {"userQuery": user_query}
56
- if known_data is not None:
57
- body["knownData"] = known_data
58
- return body
59
-
60
- @staticmethod
61
- def _parse_retrieve(resp):
62
- try:
63
- return parse(resp.json())
64
- except Exception:
65
- resp.raise_for_status()
66
- raise
File without changes
File without changes
File without changes