snowleopard 0.1.0__tar.gz → 0.2.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.
@@ -0,0 +1,17 @@
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
+ }
@@ -0,0 +1,13 @@
1
+ # python
2
+ __pycache__
3
+ .venv
4
+ *.egg-info
5
+ dist
6
+
7
+ # misc
8
+ .DS_Store
9
+ .env
10
+ .idea
11
+ .vscode
12
+ snowleopard_py.iml
13
+ CLAUDE.md
@@ -0,0 +1,60 @@
1
+ # Contributing
2
+
3
+ Thanks for your interest in contributing to the Snow Leopard Python SDK!
4
+
5
+ Questions? Reach out on [Discord](https://discord.gg/WGAyr8NpEX)
6
+
7
+ ## Getting Started
8
+
9
+ ### Prerequisites
10
+
11
+ - Python 3.11 (our default dev version)
12
+ - [uv](https://docs.astral.sh/uv/) package manager
13
+
14
+ ### Setup
15
+
16
+ 0. **Fork this repository**
17
+ If you are unfamiliar contributing to open source projects, see [first-contributions](https://github.com/firstcontributions/first-contributions) for a great walkthrough.
18
+
19
+ 1. **Clone your fork**
20
+ ```bash
21
+ git clone https://github.com/SnowLeopard-AI/snowleopard_py.git
22
+ cd snowleopard_py
23
+ ```
24
+
25
+ 2. **Install dependencies**
26
+ ```bash
27
+ uv sync --dev
28
+ ```
29
+
30
+ 3. **Running tests**
31
+ ```bash
32
+ uv run pytest
33
+ ```
34
+
35
+ ### Testing with Different Python Versions
36
+
37
+ To test against a specific Python version:
38
+ ```bash
39
+ uv run --python 3.10 pytest
40
+ ```
41
+
42
+
43
+ ### Re-Recording Cassettes or Writing New Tests
44
+
45
+ Tests use cassettes to be able to easily mock snowleopard network traffic. No configuration is needed when re-running
46
+ tests, but to re-record or author new tests a .env file is needed in tests.
47
+
48
+ ```bash
49
+ touch tests/.env
50
+ ```
51
+
52
+ ```.dotenv
53
+ #SNOWLEOPARD_LOC=
54
+ #SUPERHEROES_DFID=
55
+ #SNOWLEOPARD_API_KEY=
56
+ SNOWLEOPARD_TEST_RECORD_MODE=once
57
+ ```
58
+ See [pyvcr docs](https://vcrpy.readthedocs.io/en/latest/usage.html#record-modes) for more information on recording
59
+ modes. Once will re-record only if the file is missing, so to re-record you must delete it first.
60
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Snow Leopard AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,130 @@
1
+ Metadata-Version: 2.4
2
+ Name: snowleopard
3
+ Version: 0.2.0
4
+ Summary: snowleopard.ai client library
5
+ Author-email: Snowy <hello@snowleopard.ai>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Snow Leopard AI
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ License-File: LICENSE
28
+ Classifier: Development Status :: 2 - Pre-Alpha
29
+ Classifier: Environment :: Console
30
+ Classifier: Intended Audience :: Developers
31
+ Requires-Python: >=3.8
32
+ Requires-Dist: httpx>=0.28.1
33
+ Description-Content-Type: text/markdown
34
+
35
+ # Snow Leopard SDK for Python
36
+
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.
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pip install snowleopard
45
+ ```
46
+
47
+ ## Quick Start
48
+
49
+ ```python
50
+ from snowleopard import SnowLeopardClient
51
+
52
+ # Initialize the client (or AsyncSnowLeopardClient)
53
+ client = SnowLeopardClient(api_key="your-api-key")
54
+
55
+ # Query your data in natural language
56
+ response = client.retrieve(user_query="How many users signed up last month?", datafile_id="your-datafile-id")
57
+ ```
58
+
59
+ ## Getting Started
60
+
61
+ 1. **Get your API key** from [https://auth.snowleopard.ai/account/api_keys](https://auth.snowleopard.ai/account/api_keys)
62
+ 2. **Upload your datafiles** at [https://try.snowleopard.ai](https://try.snowleopard.ai)
63
+ 3. **Set your API key** via environment variable:
64
+ ```bash
65
+ export SNOWLEOPARD_API_KEY="your-api-key"
66
+ ```
67
+
68
+ Or pass it directly to the client:
69
+
70
+ ```python
71
+ SnowLeopardClient(api_key="your-api-key")
72
+ ```
73
+
74
+ ## Usage
75
+
76
+ ### Synchronous Client
77
+
78
+ ```python
79
+ from snowleopard import SnowLeopardClient
80
+
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)
89
+ ```
90
+
91
+ ### Async Client
92
+
93
+ ```python
94
+ from snowleopard import AsyncSnowLeopardClient
95
+
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)
100
+
101
+ # Get streaming results
102
+ async for chunk in client.response(user_query="How many superheroes are there?"):
103
+ print(chunk)
104
+ ```
105
+
106
+ ### CLI
107
+
108
+ The SDK includes a command-line interface:
109
+
110
+ ```bash
111
+ pip install snowleopard
112
+ snowy retrieve --datafile <datafile-id> "How many records are there?"
113
+ snowy response --datafile <datafile-id> "Summarize the data"
114
+ ```
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
+
128
+ ## Contributing
129
+
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,59 @@
1
+ [project]
2
+ name = "snowleopard"
3
+ dynamic = ["version"]
4
+
5
+ description = "snowleopard.ai client library"
6
+
7
+ readme = "README.md"
8
+ license = {file = "LICENSE"}
9
+
10
+ authors = [
11
+ { name = "Snowy", email = "hello@snowleopard.ai" }
12
+ ]
13
+
14
+ # list of classifier strings, defined here: https://pypi.org/classifiers/
15
+ classifiers = [
16
+ "Development Status :: 2 - Pre-Alpha",
17
+ "Environment :: Console",
18
+ "Intended Audience :: Developers",
19
+ ]
20
+
21
+ requires-python = ">=3.8"
22
+
23
+ dependencies = [
24
+ "httpx>=0.28.1",
25
+ ]
26
+
27
+ [dependency-groups]
28
+ dev = [
29
+ "pytest>=8.3.5",
30
+ "pytest-asyncio>=0.24.0",
31
+ "pytest-recording>=0.13.2",
32
+ "python-dotenv>=1.0.1",
33
+ "ruff>=0.14.4",
34
+ ]
35
+
36
+ [project.scripts]
37
+ snowy = "snowleopard.cli:main"
38
+
39
+ [build-system]
40
+ requires = ["hatchling"]
41
+ build-backend = "hatchling.build"
42
+
43
+ [tool.hatch.version]
44
+ path = "src/snowleopard/__init__.py"
45
+
46
+ [tool.hatch.build.targets.sdist]
47
+ # NOTE: .gitignore cannot be excluded from the sdist, see:
48
+ # https://github.com/pypa/hatch/discussions/368
49
+ exclude = [
50
+ "/.github",
51
+ "/tests",
52
+ "/uv.lock",
53
+ "/.python-version",
54
+ ]
55
+
56
+ [tool.hatch.build.targets.wheel]
57
+ packages = [
58
+ "src/snowleopard",
59
+ ]
@@ -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.2.0"
9
+
10
+ __all__ = [
11
+ "SnowLeopardClient",
12
+ "AsyncSnowLeopardClient",
13
+ ]
@@ -0,0 +1,64 @@
1
+ # -*- coding: utf-8 -*-
2
+ # copyright 2025 Snow Leopard, Inc
3
+ # released under the MIT license - see LICENSE file
4
+
5
+ import json
6
+ from typing import AsyncGenerator, Optional, Dict, Any
7
+
8
+ import httpx
9
+ from snowleopard.client_base import SLClientBase
10
+ from snowleopard.models import ResponseDataObjects, RetrieveResponseObjects, parse
11
+
12
+
13
+ class AsyncSnowLeopardClient(SLClientBase):
14
+ client: httpx.AsyncClient
15
+
16
+ def __init__(
17
+ self,
18
+ api_key: Optional[str] = None,
19
+ timeout: Optional[httpx.Timeout] = None,
20
+ loc: str = None,
21
+ ):
22
+ config = self._config(api_key, timeout, loc)
23
+ self.client = httpx.AsyncClient(
24
+ base_url=config.loc, headers=config.headers(), timeout=config.timeout
25
+ )
26
+
27
+ async def retrieve(
28
+ self,
29
+ *,
30
+ user_query: str,
31
+ known_data: Optional[Dict[str, Any]] = None,
32
+ datafile_id: Optional[str] = None,
33
+ ) -> RetrieveResponseObjects:
34
+ resp = await self.client.post(
35
+ url=self._build_path(datafile_id, "retrieve"),
36
+ json=self._build_request_body(user_query, known_data),
37
+ )
38
+ return self._parse_retrieve(resp)
39
+
40
+ async def response(
41
+ self,
42
+ *,
43
+ known_data: Optional[Dict[str, Any]] = None,
44
+ user_query: str,
45
+ datafile_id: Optional[str] = None,
46
+ ) -> AsyncGenerator[ResponseDataObjects, None]:
47
+ async with self.client.stream(
48
+ "POST",
49
+ self._build_path(datafile_id, "response"),
50
+ json=self._build_request_body(user_query, known_data),
51
+ ) as resp:
52
+ resp.raise_for_status()
53
+ async for line in resp.aiter_lines():
54
+ yield parse(json.loads(line))
55
+
56
+ async def __aenter__(self):
57
+ await self.client.__aenter__()
58
+ return self
59
+
60
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
61
+ await self.client.__aexit__(exc_type, exc_val, exc_tb)
62
+
63
+ async def close(self):
64
+ await self.client.aclose()
@@ -0,0 +1,125 @@
1
+ # -*- coding: utf-8 -*-
2
+ # copyright 2025 Snow Leopard, Inc
3
+ # released under the MIT license - see LICENSE file
4
+
5
+ import dataclasses
6
+ import json
7
+ import sys
8
+ import argparse
9
+ from typing import List, Optional, Dict, Any
10
+
11
+ from httpx import HTTPStatusError
12
+ from snowleopard import __version__, SnowLeopardClient
13
+ from snowleopard.models import RetrieveResponseError
14
+
15
+
16
+ def _create_parser() -> argparse.ArgumentParser:
17
+ """Create and return the argument parser."""
18
+ parser = argparse.ArgumentParser(
19
+ prog="snowy", description="Snow Leopard client library CLI"
20
+ )
21
+ parser.add_argument("--apikey", "-a", required=False, help="Snow Leopard API key")
22
+ parser.add_argument("--loc", "-l", required=False, help="Snow Leopard location")
23
+ parser.add_argument(
24
+ "--version", action="version", version=f"%(prog)s {__version__}"
25
+ )
26
+
27
+ parser.set_defaults(command_func=None)
28
+ subparsers = parser.add_subparsers(
29
+ dest="command",
30
+ metavar="command",
31
+ help="run with <command name> --help for more info",
32
+ )
33
+
34
+ retrieve = subparsers.add_parser(
35
+ "retrieve", help="Retrieve data for natural language query"
36
+ )
37
+ retrieve.set_defaults(command_func=_retrieve)
38
+ response = subparsers.add_parser(
39
+ "response", help="Get streaming response for natural language query"
40
+ )
41
+ response.set_defaults(command_func=_response)
42
+
43
+ for subparser in (retrieve, response):
44
+ subparser.add_argument(
45
+ "--datafile", "-df", type=str, help="ID for playground datafile to query"
46
+ )
47
+ subparser.add_argument(
48
+ "--knownData",
49
+ "-d",
50
+ action="append",
51
+ help="Known data in key=value format (can be specified multiple times)",
52
+ )
53
+ subparser.add_argument("question", type=str, help="Natural language query")
54
+
55
+ return parser
56
+
57
+
58
+ def _parse_known_data(known_data_list: Optional[List[str]]) -> Optional[Dict[str, Any]]:
59
+ if not known_data_list:
60
+ return None
61
+
62
+ result = {}
63
+ for item in known_data_list:
64
+ if "=" not in item:
65
+ print(
66
+ f"Error: Invalid knownData format '{item}'. Expected key=value",
67
+ file=sys.stderr,
68
+ )
69
+ sys.exit(1)
70
+ key, value = item.split("=", 1)
71
+ result[key] = value
72
+ return result
73
+
74
+
75
+ def _get_client(parsed_args):
76
+ try:
77
+ client = SnowLeopardClient(api_key=parsed_args.apikey, loc=parsed_args.loc)
78
+ except Exception as e:
79
+ print(str(e), file=sys.stderr)
80
+ sys.exit(1)
81
+ return client
82
+
83
+
84
+ def _retrieve(parsed_args):
85
+ try:
86
+ known_data = _parse_known_data(parsed_args.knownData)
87
+ with _get_client(parsed_args) as client:
88
+ resp = client.retrieve(
89
+ user_query=parsed_args.question,
90
+ known_data=known_data,
91
+ datafile_id=parsed_args.datafile,
92
+ )
93
+ print(json.dumps(dataclasses.asdict(resp)))
94
+ if isinstance(resp, RetrieveResponseError):
95
+ sys.exit(1)
96
+ except HTTPStatusError as e:
97
+ print(str(e), file=sys.stderr)
98
+ sys.exit(1)
99
+
100
+
101
+ def _response(parsed_args):
102
+ try:
103
+ known_data = _parse_known_data(parsed_args.knownData)
104
+ with _get_client(parsed_args) as client:
105
+ for chunk in client.response(
106
+ known_data=known_data,
107
+ user_query=parsed_args.question,
108
+ datafile_id=parsed_args.datafile,
109
+ ):
110
+ print(json.dumps(dataclasses.asdict(chunk)))
111
+ except HTTPStatusError as e:
112
+ print(str(e), file=sys.stderr)
113
+ sys.exit(1)
114
+
115
+
116
+ def main(args: Optional[List[str]] = None) -> None:
117
+ """CLI entry point for snowleopard."""
118
+ parser = _create_parser()
119
+ parsed_args = parser.parse_args(args=args)
120
+
121
+ if parsed_args.command_func is not None:
122
+ parsed_args.command_func(parsed_args)
123
+ else:
124
+ parser.print_help(file=sys.stderr)
125
+ sys.exit(1)
@@ -0,0 +1,66 @@
1
+ # -*- coding: utf-8 -*-
2
+ # copyright 2025 Snow Leopard, Inc
3
+ # released under the MIT license - see LICENSE file
4
+
5
+ import json
6
+ from typing import Optional, Generator, Dict, Any
7
+
8
+ import httpx
9
+ from snowleopard.client_base import SLClientBase
10
+ from snowleopard.models import parse, RetrieveResponseObjects, ResponseDataObjects
11
+
12
+
13
+ class SnowLeopardClient(SLClientBase):
14
+ client: httpx.Client
15
+
16
+ def __init__(
17
+ self,
18
+ api_key: Optional[str] = None,
19
+ timeout: Optional[httpx.Timeout] = None,
20
+ loc: str = None,
21
+ ):
22
+ config = self._config(api_key, timeout, loc)
23
+ self.client = httpx.Client(
24
+ base_url=config.loc, headers=config.headers(), timeout=config.timeout
25
+ )
26
+
27
+ def retrieve(
28
+ self,
29
+ *,
30
+ user_query: str,
31
+ known_data: Optional[Dict[str, Any]] = None,
32
+ datafile_id: Optional[str] = None,
33
+ ) -> RetrieveResponseObjects:
34
+ resp = self.client.post(
35
+ url=self._build_path(datafile_id, "retrieve"),
36
+ json=self._build_request_body(user_query, known_data),
37
+ )
38
+ if resp.status_code not in (200, 409):
39
+ resp.raise_for_status()
40
+ return self._parse_retrieve(resp)
41
+
42
+ def response(
43
+ self,
44
+ *,
45
+ known_data: Optional[Dict[str, Any]] = None,
46
+ user_query: str,
47
+ datafile_id: Optional[str] = None,
48
+ ) -> Generator[ResponseDataObjects, None, None]:
49
+ with self.client.stream(
50
+ "POST",
51
+ url=self._build_path(datafile_id, "response"),
52
+ json=self._build_request_body(user_query, known_data),
53
+ ) as resp:
54
+ resp.raise_for_status()
55
+ for line in resp.iter_lines():
56
+ yield parse(json.loads(line))
57
+
58
+ def __enter__(self):
59
+ self.client.__enter__()
60
+ return self
61
+
62
+ def __exit__(self, exc_type, exc_val, exc_tb):
63
+ self.client.__exit__(exc_type, exc_val, exc_tb)
64
+
65
+ def close(self):
66
+ self.client.close()
@@ -0,0 +1,108 @@
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.models import parse
13
+
14
+
15
+ @dataclass
16
+ class SLConfig:
17
+ api_key: str
18
+ timeout: httpx.Timeout
19
+ loc: str
20
+
21
+ def headers(self):
22
+ headers = {}
23
+ if self.api_key:
24
+ headers["Authorization"] = f"Bearer {self.api_key}"
25
+ return headers
26
+
27
+
28
+ class SLClientBase:
29
+ @abstractmethod
30
+ def retrieve(
31
+ self,
32
+ *,
33
+ user_query: str,
34
+ known_data: Optional[Dict[str, Any]] = None,
35
+ datafile_id: Optional[str] = None,
36
+ ):
37
+ """
38
+ The primary for developers building AI agents that needs to retrieve data from a database directly.
39
+
40
+ Takes a natural language question (usually from the user or the agent) and returns the data required to answer
41
+ the query in an LLM-friendly object.
42
+
43
+ :param datafile_id: (optional) The playground datafile-id if hitting a Snow Leopard api directly
44
+ :param user_query: Natural language query to execute against the Playground datafile
45
+ :param known_data: Additional context about the user_query
46
+ """
47
+ ...
48
+
49
+ @abstractmethod
50
+ def response(
51
+ self,
52
+ *,
53
+ known_data: Optional[Dict[str, Any]] = None,
54
+ user_query: str,
55
+ datafile_id: Optional[str] = None,
56
+ ):
57
+ """
58
+ Takes a natural language question (usually from the user or the agent) and returns the data required to answer
59
+ the query as well as a LLM summary of the returned data.
60
+
61
+ :param datafile_id: (optional) The playground datafile-id if hitting a Snow Leopard api directly
62
+ :param user_query: Natural language query to execute against the Playground datafile
63
+ :param known_data: (optional) Additional context about the user_query
64
+ """
65
+ ...
66
+
67
+ @staticmethod
68
+ def _config(
69
+ api_key: Optional[str], timeout: Optional[httpx.Timeout], loc: Optional[str]
70
+ ) -> SLConfig:
71
+ api_key = api_key or os.environ.get("SNOWLEOPARD_API_KEY")
72
+ if api_key is None:
73
+ raise ValueError(
74
+ 'Missing required argument "api_key" and environment variable "SNOWLEOPARD_API_KEY" not set'
75
+ )
76
+ timeout = timeout or httpx.Timeout(
77
+ connect=5.0, read=600.0, write=10.0, pool=5.0
78
+ )
79
+ loc = loc or os.environ.get("SNOWLEOPARD_LOC", "https://api.snowleopard.ai")
80
+ if not loc:
81
+ raise ValueError(
82
+ 'Missing required argument "loc" and environment variable "SNOWLEOPARD_LOC" not set'
83
+ )
84
+ return SLConfig(api_key, timeout, loc)
85
+
86
+ @staticmethod
87
+ def _build_path(datafile_id: str, endpoint: str) -> str:
88
+ if datafile_id is None:
89
+ return endpoint
90
+ else:
91
+ return f"datafiles/{datafile_id}/{endpoint}"
92
+
93
+ @staticmethod
94
+ def _build_request_body(
95
+ user_query: str, known_data: Optional[Dict[str, Any]] = None
96
+ ) -> Dict[str, Any]:
97
+ body = {"userQuery": user_query}
98
+ if known_data is not None:
99
+ body["knownData"] = known_data
100
+ return body
101
+
102
+ @staticmethod
103
+ def _parse_retrieve(resp):
104
+ try:
105
+ return parse(resp.json())
106
+ except Exception:
107
+ resp.raise_for_status()
108
+ raise
@@ -0,0 +1,145 @@
1
+ # -*- coding: utf-8 -*-
2
+ # copyright 2025 Snow Leopard, Inc
3
+ # released under the MIT license - see LICENSE file
4
+
5
+ from __future__ import annotations
6
+
7
+ from dataclasses import dataclass, fields
8
+ from enum import Enum
9
+ from typing import Any, Dict, List, Optional, Union
10
+
11
+
12
+ class StrEnum(str, Enum):
13
+ """String enum compatible with Python 3.8"""
14
+
15
+ pass
16
+
17
+
18
+ @dataclass
19
+ class RetrieveResponse:
20
+ objType = "retrieveResponse"
21
+
22
+ callId: str
23
+ data: List[Union[SchemaData, ErrorSchemaData]]
24
+ responseStatus: ResponseStatus
25
+
26
+
27
+ @dataclass
28
+ class RetrieveResponseError:
29
+ objType = "apiError"
30
+
31
+ callId: str
32
+ responseStatus: str
33
+ description: str
34
+
35
+
36
+ @dataclass
37
+ class SchemaData:
38
+ objType = "schemaData"
39
+
40
+ schemaId: str
41
+ schemaType: str
42
+ query: str
43
+ rows: List[Dict[str, Any]]
44
+ querySummary: Dict[str, Any]
45
+ rowMax: int
46
+ isTrimmed: bool
47
+ callId: str = None
48
+
49
+
50
+ @dataclass
51
+ class ErrorSchemaData:
52
+ objType = "errorSchemaData"
53
+
54
+ schemaType: str
55
+ schemaId: str
56
+ query: str
57
+ error: str
58
+ querySummary: Dict[str, Any]
59
+ datastoreExceptionInfo: Optional[str] = None
60
+ callId: str = None
61
+
62
+
63
+ @dataclass
64
+ class ResponseStart:
65
+ objType = "responseStart"
66
+
67
+ callId: str
68
+ userQuery: str
69
+
70
+
71
+ @dataclass
72
+ class ResponseData:
73
+ objType = "responseData"
74
+
75
+ callId: str
76
+ data: List[Union[SchemaData, ErrorSchemaData]]
77
+
78
+
79
+ @dataclass
80
+ class EarlyTermination:
81
+ objType = "earlyTermination"
82
+
83
+ callId: str
84
+ responseStatus: ResponseStatus
85
+ reason: str
86
+ extra: dict
87
+
88
+
89
+ @dataclass
90
+ class ResponseLLMResult:
91
+ objType = "responseResult"
92
+
93
+ callId: str
94
+ responseStatus: ResponseStatus
95
+ llmResponse: Dict[str, Any]
96
+
97
+
98
+ class ResponseStatus(StrEnum):
99
+ SUCCESS = "SUCCESS"
100
+ NOT_FOUND_IN_SCHEMA = "NOT_FOUND_IN_SCHEMA"
101
+ UNKNOWN = "UNKNOWN"
102
+ INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR"
103
+ AUTHORIZATION_FAILED = "AUTHORIZATION_FAILED"
104
+ LLM_ERROR = "LLM_ERROR"
105
+ LLM_TOKEN_LIMIT_REACHED = "LLM_TOKEN_LIMIT_REACHED"
106
+
107
+
108
+ _PARSE_OBJS = {
109
+ o.objType: o
110
+ for o in (
111
+ RetrieveResponse,
112
+ RetrieveResponseError,
113
+ SchemaData,
114
+ ErrorSchemaData,
115
+ ResponseStart,
116
+ ResponseData,
117
+ EarlyTermination,
118
+ ResponseLLMResult,
119
+ )
120
+ }
121
+
122
+ RetrieveResponseObjects = Union[RetrieveResponse, RetrieveResponseError]
123
+
124
+ ResponseDataObjects = Union[
125
+ ErrorSchemaData,
126
+ ResponseStart,
127
+ ResponseData,
128
+ EarlyTermination,
129
+ ResponseLLMResult,
130
+ ]
131
+
132
+
133
+ def parse(obj):
134
+ if isinstance(obj, dict):
135
+ kind = _PARSE_OBJS.get(obj.get("__type__"))
136
+ if kind:
137
+ keys = {f.name for f in fields(kind)}
138
+ kwargs = {k: parse(v) for k, v in obj.items() if k in keys}
139
+ return kind(**kwargs)
140
+ else:
141
+ return obj
142
+ elif isinstance(obj, list):
143
+ return [parse(v) for v in obj]
144
+ else:
145
+ return obj
snowleopard-0.1.0/LICENSE DELETED
@@ -1,2 +0,0 @@
1
- copyright 2024 Snow Leopard, Inc
2
- all rights reserved
@@ -1,16 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: snowleopard
3
- Version: 0.1.0
4
- Summary: snowleopard.ai client library
5
- License: copyright 2024 Snow Leopard, Inc
6
- all rights reserved
7
-
8
- Classifier: Development Status :: 1 - Planning
9
- Classifier: Environment :: Console
10
- Classifier: Intended Audience :: Developers
11
- Requires-Python: >=3.8
12
- Description-Content-Type: text/markdown
13
- License-File: LICENSE
14
- Requires-Dist: importlib_metadata>=4.6; python_version < "3.10"
15
-
16
- # SnowLeopard-ai python client library
@@ -1 +0,0 @@
1
- # SnowLeopard-ai python client library
@@ -1,39 +0,0 @@
1
-
2
-
3
- [build-system]
4
- requires = ["setuptools>=61.0.0", "wheel"]
5
- build-backend = "setuptools.build_meta"
6
-
7
-
8
- [project]
9
- name = "snowleopard"
10
- dynamic = ["version"]
11
-
12
- description = "snowleopard.ai client library"
13
-
14
- readme = "README.md"
15
- license = {file = "LICENSE"}
16
-
17
- # list of classifier strings, defined here: https://pypi.org/classifiers/
18
- classifiers = [
19
- "Development Status :: 1 - Planning",
20
- "Environment :: Console",
21
- "Intended Audience :: Developers",
22
- ]
23
-
24
- # see https://devguide.python.org/versions/#versions for end-of-life dates
25
- # next update: 3.8 EoL is 2024-10
26
- requires-python = ">=3.8"
27
-
28
- dependencies = [
29
- "importlib_metadata >= 4.6 ; python_version < '3.10'"
30
- ]
31
-
32
-
33
- [tool.setuptools]
34
-
35
- [tool.setuptools.packages.find]
36
- where = ["src"]
37
-
38
- [tool.setuptools.dynamic]
39
- version = { attr = "snowleopard.__version__" }
@@ -1,4 +0,0 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
@@ -1,5 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # copyright 2024 Snow Leopard, Inc - all rights reserved
3
-
4
-
5
- __version__ = '0.1.0'
@@ -1,16 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: snowleopard
3
- Version: 0.1.0
4
- Summary: snowleopard.ai client library
5
- License: copyright 2024 Snow Leopard, Inc
6
- all rights reserved
7
-
8
- Classifier: Development Status :: 1 - Planning
9
- Classifier: Environment :: Console
10
- Classifier: Intended Audience :: Developers
11
- Requires-Python: >=3.8
12
- Description-Content-Type: text/markdown
13
- License-File: LICENSE
14
- Requires-Dist: importlib_metadata>=4.6; python_version < "3.10"
15
-
16
- # SnowLeopard-ai python client library
@@ -1,9 +0,0 @@
1
- LICENSE
2
- README.md
3
- pyproject.toml
4
- src/snowleopard/__init__.py
5
- src/snowleopard.egg-info/PKG-INFO
6
- src/snowleopard.egg-info/SOURCES.txt
7
- src/snowleopard.egg-info/dependency_links.txt
8
- src/snowleopard.egg-info/requires.txt
9
- src/snowleopard.egg-info/top_level.txt
@@ -1,3 +0,0 @@
1
-
2
- [:python_version < "3.10"]
3
- importlib_metadata>=4.6
@@ -1 +0,0 @@
1
- snowleopard