seltz 0.1.2__py3-none-any.whl → 0.1.4__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.
- seltz/__init__.py +7 -0
- seltz/client.py +3 -1
- seltz/exceptions.py +4 -2
- seltz/seltz.py +39 -8
- seltz/services/search_service.py +30 -9
- seltz/types.py +285 -0
- {seltz-0.1.2.dist-info → seltz-0.1.4.dist-info}/METADATA +119 -7
- seltz-0.1.4.dist-info/RECORD +14 -0
- seltz_public_api/proto/v1/seltz_pb2.py +12 -12
- seltz_public_api/proto/v1/seltz_pb2.pyi +9 -12
- seltz-0.1.2.dist-info/RECORD +0 -13
- {seltz-0.1.2.dist-info → seltz-0.1.4.dist-info}/WHEEL +0 -0
- {seltz-0.1.2.dist-info → seltz-0.1.4.dist-info}/top_level.txt +0 -0
seltz/__init__.py
CHANGED
|
@@ -10,9 +10,16 @@ from .exceptions import (
|
|
|
10
10
|
SeltzTimeoutError,
|
|
11
11
|
)
|
|
12
12
|
from .seltz import Seltz
|
|
13
|
+
from .types import Document, Includes, SearchResult
|
|
13
14
|
|
|
14
15
|
__all__ = [
|
|
16
|
+
# Main client
|
|
15
17
|
"Seltz",
|
|
18
|
+
# Types
|
|
19
|
+
"Includes",
|
|
20
|
+
"SearchResult",
|
|
21
|
+
"Document",
|
|
22
|
+
# Exceptions
|
|
16
23
|
"SeltzError",
|
|
17
24
|
"SeltzConfigurationError",
|
|
18
25
|
"SeltzAuthenticationError",
|
seltz/client.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
1
3
|
import grpc
|
|
2
4
|
|
|
3
5
|
|
|
@@ -5,7 +7,7 @@ class SeltzClient:
|
|
|
5
7
|
"""Low-level gRPC client for Seltz API."""
|
|
6
8
|
|
|
7
9
|
def __init__(
|
|
8
|
-
self, endpoint: str, api_key: str
|
|
10
|
+
self, endpoint: str, api_key: Optional[str] = None, insecure: bool = False
|
|
9
11
|
):
|
|
10
12
|
"""Initialize the Seltz gRPC client.
|
|
11
13
|
|
seltz/exceptions.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Custom exceptions for the Seltz SDK."""
|
|
2
2
|
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
3
5
|
import grpc
|
|
4
6
|
|
|
5
7
|
|
|
@@ -33,8 +35,8 @@ class SeltzAPIError(SeltzError):
|
|
|
33
35
|
def __init__(
|
|
34
36
|
self,
|
|
35
37
|
message: str,
|
|
36
|
-
grpc_code: grpc.StatusCode
|
|
37
|
-
grpc_details: str
|
|
38
|
+
grpc_code: Optional[grpc.StatusCode] = None,
|
|
39
|
+
grpc_details: Optional[str] = None,
|
|
38
40
|
):
|
|
39
41
|
super().__init__(message)
|
|
40
42
|
self.grpc_code = grpc_code
|
seltz/seltz.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from typing import Optional
|
|
2
3
|
|
|
3
4
|
from .client import SeltzClient
|
|
4
5
|
from .exceptions import SeltzConfigurationError
|
|
5
|
-
from .services import SearchResponse
|
|
6
6
|
from .services.search_service import SearchService
|
|
7
|
+
from .types import Includes, SearchResult
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class Seltz:
|
|
@@ -13,7 +14,7 @@ class Seltz:
|
|
|
13
14
|
|
|
14
15
|
def __init__(
|
|
15
16
|
self,
|
|
16
|
-
api_key: str
|
|
17
|
+
api_key: Optional[str] = os.environ.get("SELTZ_API_KEY"),
|
|
17
18
|
endpoint: str = _ENDPOINT,
|
|
18
19
|
insecure: bool = False,
|
|
19
20
|
):
|
|
@@ -32,17 +33,47 @@ class Seltz:
|
|
|
32
33
|
"""
|
|
33
34
|
if api_key is None:
|
|
34
35
|
raise SeltzConfigurationError("No API key provided")
|
|
35
|
-
self._client = SeltzClient(
|
|
36
|
+
self._client = SeltzClient(
|
|
37
|
+
endpoint=endpoint, api_key=api_key, insecure=insecure
|
|
38
|
+
)
|
|
36
39
|
self._search = SearchService(self._client.channel, self._client.api_key)
|
|
37
40
|
|
|
38
|
-
def search(
|
|
41
|
+
def search(
|
|
42
|
+
self,
|
|
43
|
+
query: str,
|
|
44
|
+
*,
|
|
45
|
+
includes: Optional[Includes] = None,
|
|
46
|
+
context: Optional[str] = None,
|
|
47
|
+
profile: Optional[str] = None,
|
|
48
|
+
) -> SearchResult:
|
|
39
49
|
"""Perform a search query.
|
|
40
50
|
|
|
51
|
+
This method searches using the Seltz AI-powered search engine. You can
|
|
52
|
+
customize the search behavior using optional parameters.
|
|
53
|
+
|
|
41
54
|
Args:
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
query: The search query string. Keep this concise for best performance.
|
|
56
|
+
includes: Configuration for what to include in results. If not specified
|
|
57
|
+
server defaults are used.
|
|
58
|
+
context: Additional context for the query. Include as much relevant
|
|
59
|
+
information as feasible to improve search quality. For example,
|
|
60
|
+
"user is looking for Python documentation for beginners".
|
|
61
|
+
profile: Search profile to use. Different profiles may use different
|
|
62
|
+
ranking algorithms or have different data source preferences.
|
|
44
63
|
|
|
45
64
|
Returns:
|
|
46
|
-
|
|
65
|
+
SearchResult: Search results containing documents and metadata.
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
SeltzAuthenticationError: If API key is invalid
|
|
69
|
+
SeltzConnectionError: If connection to API fails
|
|
70
|
+
SeltzTimeoutError: If request times out
|
|
71
|
+
SeltzRateLimitError: If rate limit is exceeded
|
|
72
|
+
SeltzAPIError: For other API errors
|
|
47
73
|
"""
|
|
48
|
-
return self._search.search(
|
|
74
|
+
return self._search.search(
|
|
75
|
+
query=query,
|
|
76
|
+
includes=includes,
|
|
77
|
+
context=context,
|
|
78
|
+
profile=profile,
|
|
79
|
+
)
|
seltz/services/search_service.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
1
3
|
import grpc
|
|
2
4
|
|
|
3
5
|
from ..exceptions import (
|
|
@@ -7,13 +9,14 @@ from ..exceptions import (
|
|
|
7
9
|
SeltzRateLimitError,
|
|
8
10
|
SeltzTimeoutError,
|
|
9
11
|
)
|
|
10
|
-
from
|
|
12
|
+
from ..types import Includes, SearchResult
|
|
13
|
+
from . import SearchRequest, SeltzServiceStub
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
class SearchService:
|
|
14
17
|
"""Service for performing search operations via gRPC."""
|
|
15
18
|
|
|
16
|
-
def __init__(self, channel: grpc.Channel, api_key: str
|
|
19
|
+
def __init__(self, channel: grpc.Channel, api_key: Optional[str]):
|
|
17
20
|
"""Initialize the search service.
|
|
18
21
|
|
|
19
22
|
Args:
|
|
@@ -23,28 +26,46 @@ class SearchService:
|
|
|
23
26
|
self._stub = SeltzServiceStub(channel)
|
|
24
27
|
self._api_key = api_key
|
|
25
28
|
|
|
26
|
-
def search(
|
|
29
|
+
def search(
|
|
30
|
+
self,
|
|
31
|
+
query: str,
|
|
32
|
+
*,
|
|
33
|
+
includes: Optional[Includes] = None,
|
|
34
|
+
context: Optional[str] = None,
|
|
35
|
+
profile: Optional[str] = None,
|
|
36
|
+
) -> SearchResult:
|
|
27
37
|
"""Perform a search query.
|
|
28
38
|
|
|
29
39
|
Args:
|
|
30
40
|
query: The search query string
|
|
31
|
-
|
|
41
|
+
includes: Either an Includes object
|
|
42
|
+
context: Additional context for the query
|
|
43
|
+
profile: Search profile to use
|
|
32
44
|
|
|
33
45
|
Returns:
|
|
34
|
-
|
|
46
|
+
SearchResult: Wrapped search response with helper methods
|
|
35
47
|
|
|
36
48
|
Raises:
|
|
37
|
-
|
|
49
|
+
SeltzAuthenticationError: If authentication fails
|
|
50
|
+
SeltzConnectionError: If connection fails
|
|
51
|
+
SeltzTimeoutError: If request times out
|
|
52
|
+
SeltzRateLimitError: If rate limit exceeded
|
|
53
|
+
SeltzAPIError: For other API errors
|
|
38
54
|
"""
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
req = SearchRequest(
|
|
56
|
+
query=query,
|
|
57
|
+
includes=includes._to_protobuf() if includes is not None else None,
|
|
58
|
+
context=context,
|
|
59
|
+
profile=profile,
|
|
60
|
+
api_key=self._api_key,
|
|
61
|
+
)
|
|
41
62
|
|
|
42
63
|
metadata = []
|
|
43
64
|
if self._api_key:
|
|
44
65
|
metadata.append(("authorization", f"Bearer {self._api_key}"))
|
|
45
66
|
|
|
46
67
|
try:
|
|
47
|
-
return self._stub.Search(req, metadata=metadata, timeout=30)
|
|
68
|
+
return SearchResult(self._stub.Search(req, metadata=metadata, timeout=30))
|
|
48
69
|
except grpc.RpcError as e:
|
|
49
70
|
if e.code() == grpc.StatusCode.UNAUTHENTICATED:
|
|
50
71
|
raise SeltzAuthenticationError(
|
seltz/types.py
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""Type definitions for the Seltz SDK."""
|
|
2
|
+
|
|
3
|
+
from typing import Iterator, Optional
|
|
4
|
+
|
|
5
|
+
from seltz_public_api.proto.v1.seltz_pb2 import (
|
|
6
|
+
Document as PbDocument,
|
|
7
|
+
)
|
|
8
|
+
from seltz_public_api.proto.v1.seltz_pb2 import (
|
|
9
|
+
Includes as PbIncludes,
|
|
10
|
+
)
|
|
11
|
+
from seltz_public_api.proto.v1.seltz_pb2 import (
|
|
12
|
+
SearchResponse as PbSearchResponse,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Includes:
|
|
17
|
+
"""Configuration for what to include in search results.
|
|
18
|
+
|
|
19
|
+
This class provides a user-friendly interface for configuring search result
|
|
20
|
+
inclusions, wrapping the underlying protobuf Includes message.
|
|
21
|
+
|
|
22
|
+
Supports both constructor and fluent builder patterns:
|
|
23
|
+
|
|
24
|
+
Constructor pattern (simple):
|
|
25
|
+
>>> includes = Includes(max_documents=5)
|
|
26
|
+
>>> response = client.search("query", includes=includes)
|
|
27
|
+
|
|
28
|
+
Fluent builder pattern (for multiple fields):
|
|
29
|
+
>>> includes = Includes().max_documents(5)
|
|
30
|
+
>>> response = client.search("query", includes=includes)
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
max_documents: Maximum number of documents to return in the response.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
max_documents: int = 10,
|
|
39
|
+
):
|
|
40
|
+
"""Initialize the Includes configuration.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
max_documents: Maximum number of documents to return. If not specified,
|
|
44
|
+
the server will use its default value.
|
|
45
|
+
"""
|
|
46
|
+
self._max_documents = max_documents
|
|
47
|
+
|
|
48
|
+
def max_documents(self, value: int) -> "Includes":
|
|
49
|
+
"""Set maximum number of documents (fluent builder method).
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
value: Maximum number of documents to return.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Self for method chaining.
|
|
56
|
+
|
|
57
|
+
Example:
|
|
58
|
+
>>> includes = Includes().max_documents(10)
|
|
59
|
+
>>> # Or chain multiple fields (when more fields are available)
|
|
60
|
+
>>> includes = Includes().max_documents(10).include_metadata(True)
|
|
61
|
+
"""
|
|
62
|
+
self._max_documents = value
|
|
63
|
+
return self
|
|
64
|
+
|
|
65
|
+
def _to_protobuf(self) -> PbIncludes:
|
|
66
|
+
"""Convert to protobuf Includes message.
|
|
67
|
+
|
|
68
|
+
Internal method used by the SDK to convert this user-facing object
|
|
69
|
+
to the underlying protobuf representation.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
PbIncludes: Protobuf Includes message.
|
|
73
|
+
"""
|
|
74
|
+
pb_includes = PbIncludes()
|
|
75
|
+
pb_includes.max_documents = self._max_documents
|
|
76
|
+
return pb_includes
|
|
77
|
+
|
|
78
|
+
def __repr__(self) -> str:
|
|
79
|
+
"""String representation for debugging."""
|
|
80
|
+
params = []
|
|
81
|
+
params.append(f"max_documents={self._max_documents}")
|
|
82
|
+
return f"Includes({', '.join(params)})"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class Document:
|
|
86
|
+
"""A search result document.
|
|
87
|
+
|
|
88
|
+
This class wraps the protobuf Document message with a more user-friendly
|
|
89
|
+
interface and helper methods.
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
>>> doc = response.documents[0]
|
|
93
|
+
>>> print(doc.url)
|
|
94
|
+
>>> print(doc.content[:100])
|
|
95
|
+
>>> if doc.has_content():
|
|
96
|
+
... print("Document has content")
|
|
97
|
+
|
|
98
|
+
Attributes:
|
|
99
|
+
url: URL of the document (may be None for non-web documents)
|
|
100
|
+
content: Content/snippet of the document
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def __init__(self, pb_document: PbDocument):
|
|
104
|
+
"""Initialize from protobuf Document.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
pb_document: Protobuf Document message
|
|
108
|
+
"""
|
|
109
|
+
self._pb = pb_document
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def url(self) -> Optional[str]:
|
|
113
|
+
"""Get the document URL.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
URL string if available, None otherwise.
|
|
117
|
+
"""
|
|
118
|
+
return self._pb.url if self._pb.HasField("url") else None
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def content(self) -> Optional[str]:
|
|
122
|
+
"""Get the document content.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Content string if available, None otherwise.
|
|
126
|
+
"""
|
|
127
|
+
return self._pb.content if self._pb.HasField("content") else None
|
|
128
|
+
|
|
129
|
+
def has_url(self) -> bool:
|
|
130
|
+
"""Check if document has a URL.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
True if URL is present, False otherwise.
|
|
134
|
+
"""
|
|
135
|
+
return self._pb.HasField("url")
|
|
136
|
+
|
|
137
|
+
def has_content(self) -> bool:
|
|
138
|
+
"""Check if document has content.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
True if content is present, False otherwise.
|
|
142
|
+
"""
|
|
143
|
+
return self._pb.HasField("content")
|
|
144
|
+
|
|
145
|
+
def to_dict(self) -> dict:
|
|
146
|
+
"""Convert document to a dictionary.
|
|
147
|
+
|
|
148
|
+
Useful for JSON serialization or logging.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Dictionary with url and content fields (only if present).
|
|
152
|
+
"""
|
|
153
|
+
result = {}
|
|
154
|
+
if self.has_url():
|
|
155
|
+
result["url"] = self.url
|
|
156
|
+
if self.has_content():
|
|
157
|
+
result["content"] = self.content
|
|
158
|
+
return result
|
|
159
|
+
|
|
160
|
+
def __repr__(self) -> str:
|
|
161
|
+
"""String representation for debugging."""
|
|
162
|
+
parts = []
|
|
163
|
+
if self.has_url():
|
|
164
|
+
parts.append(f"url='{self.url}'")
|
|
165
|
+
if self.content is not None:
|
|
166
|
+
content_preview = (
|
|
167
|
+
self.content[:50] + "..." if len(self.content) > 50 else self.content
|
|
168
|
+
)
|
|
169
|
+
parts.append(f"content='{content_preview}'")
|
|
170
|
+
return f"Document({', '.join(parts)})"
|
|
171
|
+
|
|
172
|
+
def __str__(self) -> str:
|
|
173
|
+
"""Human-readable string representation."""
|
|
174
|
+
if self.url is not None:
|
|
175
|
+
return f"{self.url}: {self.content[:100] if self.content is not None else 'No content'}"
|
|
176
|
+
return self.content if self.content is not None else "Empty document"
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class SearchResult:
|
|
180
|
+
"""Search result containing documents and metadata.
|
|
181
|
+
|
|
182
|
+
This class wraps the protobuf SearchResponse message with a more user-friendly
|
|
183
|
+
interface and helper methods.
|
|
184
|
+
|
|
185
|
+
Example:
|
|
186
|
+
>>> result = client.search("Python tutorial")
|
|
187
|
+
>>> print(f"Found {len(result)} documents")
|
|
188
|
+
>>> for doc in result:
|
|
189
|
+
... print(doc.url)
|
|
190
|
+
>>> first = result.first()
|
|
191
|
+
>>> urls = result.get_urls()
|
|
192
|
+
|
|
193
|
+
Attributes:
|
|
194
|
+
documents: List of Document objects in the search results
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
def __init__(self, pb_response: PbSearchResponse):
|
|
198
|
+
"""Initialize from protobuf SearchResponse.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
pb_response: Protobuf SearchResponse message
|
|
202
|
+
"""
|
|
203
|
+
self._pb = pb_response
|
|
204
|
+
self._documents = [Document(pb_doc) for pb_doc in pb_response.documents]
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def documents(self) -> list[Document]:
|
|
208
|
+
"""Get the list of documents.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
List of Document objects.
|
|
212
|
+
"""
|
|
213
|
+
return self._documents
|
|
214
|
+
|
|
215
|
+
def __len__(self) -> int:
|
|
216
|
+
"""Get the number of documents.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Number of documents in the result.
|
|
220
|
+
"""
|
|
221
|
+
return len(self._documents)
|
|
222
|
+
|
|
223
|
+
def __iter__(self) -> Iterator[Document]:
|
|
224
|
+
"""Iterate over documents.
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Iterator over Document objects.
|
|
228
|
+
"""
|
|
229
|
+
return iter(self._documents)
|
|
230
|
+
|
|
231
|
+
def __getitem__(self, index: int) -> Document:
|
|
232
|
+
"""Get document by index.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
index: Index of the document (supports negative indexing)
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
Document at the specified index.
|
|
239
|
+
|
|
240
|
+
Raises:
|
|
241
|
+
IndexError: If index is out of range.
|
|
242
|
+
"""
|
|
243
|
+
return self._documents[index]
|
|
244
|
+
|
|
245
|
+
def first(self) -> Optional[Document]:
|
|
246
|
+
"""Get the first document.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
First document if available, None if result is empty.
|
|
250
|
+
"""
|
|
251
|
+
return self._documents[0] if self._documents else None
|
|
252
|
+
|
|
253
|
+
def get_urls(self) -> list[str]:
|
|
254
|
+
"""Get all document URLs.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
List of URLs from documents that have URLs.
|
|
258
|
+
"""
|
|
259
|
+
return [doc.url for doc in self._documents if doc.url is not None]
|
|
260
|
+
|
|
261
|
+
def get_contents(self) -> list[str]:
|
|
262
|
+
"""Get all document contents.
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
List of content strings from documents that have content.
|
|
266
|
+
"""
|
|
267
|
+
return [doc.content for doc in self._documents if doc.content is not None]
|
|
268
|
+
|
|
269
|
+
def to_dict(self) -> dict:
|
|
270
|
+
"""Convert result to a dictionary.
|
|
271
|
+
|
|
272
|
+
Useful for JSON serialization or logging.
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Dictionary with documents list.
|
|
276
|
+
"""
|
|
277
|
+
return {"documents": [doc.to_dict() for doc in self._documents]}
|
|
278
|
+
|
|
279
|
+
def __repr__(self) -> str:
|
|
280
|
+
"""String representation for debugging."""
|
|
281
|
+
return f"SearchResult(documents={len(self._documents)})"
|
|
282
|
+
|
|
283
|
+
def __str__(self) -> str:
|
|
284
|
+
"""Human-readable string representation."""
|
|
285
|
+
return f"SearchResult with {len(self._documents)} document(s)"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: seltz
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Seltz Python SDK for AI-powered search
|
|
5
5
|
Author-email: Seltz <support@seltz.ai>
|
|
6
6
|
Project-URL: Homepage, https://seltz.ai
|
|
@@ -21,7 +21,7 @@ Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
|
21
21
|
Requires-Python: >=3.9
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
Requires-Dist: grpcio>=1.76.0
|
|
24
|
-
Requires-Dist: protobuf
|
|
24
|
+
Requires-Dist: protobuf<6.0
|
|
25
25
|
|
|
26
26
|
# Seltz Python SDK
|
|
27
27
|
|
|
@@ -77,15 +77,127 @@ Creates a new Seltz client instance.
|
|
|
77
77
|
|
|
78
78
|
**Returns:** `Seltz` instance
|
|
79
79
|
|
|
80
|
-
### `client.search(
|
|
80
|
+
### `client.search(query, *, includes=None, context=None, profile=None)`
|
|
81
81
|
|
|
82
82
|
Performs a search query.
|
|
83
83
|
|
|
84
84
|
**Parameters:**
|
|
85
|
-
- `
|
|
86
|
-
- `
|
|
85
|
+
- `query` (str): The search query text. Keep concise for best performance.
|
|
86
|
+
- `includes` (Includes | int, optional): What to include in results.
|
|
87
|
+
- `context` (str, optional): Additional context for the query. Include as much relevant information as feasible to improve search quality.
|
|
88
|
+
- `profile` (str, optional): Search profile to use (contact support for available profiles).
|
|
87
89
|
|
|
88
|
-
**Returns:** `
|
|
90
|
+
**Returns:** `SearchResult` with documents and helper methods.
|
|
91
|
+
|
|
92
|
+
**Examples:**
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
response = client.search("Python asyncio tutorial")
|
|
96
|
+
|
|
97
|
+
# With Includes configuration
|
|
98
|
+
from seltz import Includes
|
|
99
|
+
response = client.search(
|
|
100
|
+
"Python tutorial",
|
|
101
|
+
includes=Includes(max_documents=10)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Access results
|
|
105
|
+
for doc in response:
|
|
106
|
+
print(f"URL: {doc.url}")
|
|
107
|
+
print(f"Content: {doc.content}")
|
|
108
|
+
|
|
109
|
+
# Or use helper methods
|
|
110
|
+
first_doc = response.first()
|
|
111
|
+
all_urls = response.get_urls()
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `Includess`
|
|
115
|
+
|
|
116
|
+
Configuration for what to include in search results.
|
|
117
|
+
|
|
118
|
+
**Parameters:**
|
|
119
|
+
- `max_documents` (int, optional): Maximum number of documents to return.
|
|
120
|
+
|
|
121
|
+
**Constructor Pattern (simple cases):**
|
|
122
|
+
```python
|
|
123
|
+
from seltz import Includes
|
|
124
|
+
|
|
125
|
+
# Simple constructor
|
|
126
|
+
includes = Includes(max_documents=5)
|
|
127
|
+
response = client.search("query", includes=includes)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Fluent Builder Pattern (complex cases):**
|
|
131
|
+
```python
|
|
132
|
+
from seltz import Includes
|
|
133
|
+
|
|
134
|
+
# Single field
|
|
135
|
+
includes = Includes().max_documents(10)
|
|
136
|
+
|
|
137
|
+
# Mixed approach
|
|
138
|
+
includes = Includes(max_documents=5).max_documents(10)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### `SearchResult`
|
|
142
|
+
|
|
143
|
+
Search result wrapper with helper methods.
|
|
144
|
+
|
|
145
|
+
**Properties:**
|
|
146
|
+
- `documents`: List of Document objects
|
|
147
|
+
|
|
148
|
+
**Methods:**
|
|
149
|
+
- `__len__()`: Get number of documents (`len(result)`)
|
|
150
|
+
- `__iter__()`: Iterate over documents (`for doc in result:`)
|
|
151
|
+
- `__getitem__(index)`: Get document by index (`result[0]`, `result[-1]`)
|
|
152
|
+
- `first()`: Get first document (or None if empty)
|
|
153
|
+
- `get_urls()`: Get list of all URLs
|
|
154
|
+
- `get_contents()`: Get list of all contents
|
|
155
|
+
- `to_dict()`: Convert to dictionary for JSON serialization
|
|
156
|
+
|
|
157
|
+
**Example:**
|
|
158
|
+
```python
|
|
159
|
+
result = client.search("Python tutorial")
|
|
160
|
+
|
|
161
|
+
print(f"Found {len(result)} documents")
|
|
162
|
+
|
|
163
|
+
for doc in result:
|
|
164
|
+
print(doc.url)
|
|
165
|
+
|
|
166
|
+
first = result.first()
|
|
167
|
+
if first:
|
|
168
|
+
print(f"Top result: {first.url}")
|
|
169
|
+
|
|
170
|
+
urls = result.get_urls()
|
|
171
|
+
|
|
172
|
+
import json
|
|
173
|
+
json_data = json.dumps(result.to_dict())
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### `Document`
|
|
177
|
+
|
|
178
|
+
Individual search result document.
|
|
179
|
+
|
|
180
|
+
**Properties:**
|
|
181
|
+
- `url`: Document URL (optional)
|
|
182
|
+
- `content`: Document content (optional)
|
|
183
|
+
|
|
184
|
+
**Methods:**
|
|
185
|
+
- `has_url()`: Check if URL exists
|
|
186
|
+
- `has_content()`: Check if content exists
|
|
187
|
+
- `to_dict()`: Convert to dictionary
|
|
188
|
+
|
|
189
|
+
**Example:**
|
|
190
|
+
```python
|
|
191
|
+
doc = result[0]
|
|
192
|
+
|
|
193
|
+
if doc.has_url():
|
|
194
|
+
print(f"URL: {doc.url}")
|
|
195
|
+
|
|
196
|
+
if doc.has_content():
|
|
197
|
+
print(f"Content: {doc.content[:100]}")
|
|
198
|
+
|
|
199
|
+
doc_dict = doc.to_dict()
|
|
200
|
+
```
|
|
89
201
|
|
|
90
202
|
## Error Handling
|
|
91
203
|
|
|
@@ -121,4 +233,4 @@ except SeltzAPIError as e:
|
|
|
121
233
|
|
|
122
234
|
- Python 3.8+
|
|
123
235
|
- grpcio >= 1.76.0
|
|
124
|
-
- protobuf
|
|
236
|
+
- protobuf < 6.0
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
seltz/__init__.py,sha256=oFZzZxyentKvxIODB2Ijt28SFBU_UhM9GPBbe6olhoQ,642
|
|
2
|
+
seltz/client.py,sha256=6NsMoa6UpYfR6aBjwICExHBqm2KDdKc5E_f-gjna-40,1238
|
|
3
|
+
seltz/exceptions.py,sha256=oDPXlRfUVLcQGTj17jY7iTQ7YGy0YCRCAq6Cxc9ZgGo,1029
|
|
4
|
+
seltz/seltz.py,sha256=dOzLFuEWxSV1LhcgzzjPUeihsL_-j5Xoo7CQEfYwXbk,2841
|
|
5
|
+
seltz/types.py,sha256=1Es8aV4xvJJo-OXrsXgMyvYZMMm6kAU2l7Pxjs8avz4,8382
|
|
6
|
+
seltz/services/__init__.py,sha256=hPncxVXXYyEjvBCwe0a6l4KGOKfKuarSONL1MjSekmk,457
|
|
7
|
+
seltz/services/search_service.py,sha256=CK8s68NlBVoTieVtgcMIAQuAQoATSdKvE_S3oWLxQFk,2808
|
|
8
|
+
seltz_public_api/proto/v1/seltz_pb2.py,sha256=_xU5gknqT9TgfzB3PYc7v7QqQDO9de5bldsStygwbqk,2684
|
|
9
|
+
seltz_public_api/proto/v1/seltz_pb2.pyi,sha256=UnMchkgCad9csmvhYYww5Koc-Fko-K2LgTNgHDQ4bnY,1719
|
|
10
|
+
seltz_public_api/proto/v1/seltz_pb2_grpc.py,sha256=Gt-sDKmEsNYzRbzXKvQKIDFoVVnjU8LtEm_sbKyTuro,3024
|
|
11
|
+
seltz-0.1.4.dist-info/METADATA,sha256=VppB8wJoGKefBV2e3wr_JcOCN7-JMab2ni8peGx4Kc8,5762
|
|
12
|
+
seltz-0.1.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
13
|
+
seltz-0.1.4.dist-info/top_level.txt,sha256=omV6Wwfxo_CgGHOAIE8aKEwm2-3c4ETyBCP-0ZIaZs0,23
|
|
14
|
+
seltz-0.1.4.dist-info/RECORD,,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
3
|
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
4
|
# source: seltz_public_api/proto/v1/seltz.proto
|
|
5
|
-
# Protobuf Python Version:
|
|
5
|
+
# Protobuf Python Version: 5.29.5
|
|
6
6
|
"""Generated protocol buffer code."""
|
|
7
7
|
from google.protobuf import descriptor as _descriptor
|
|
8
8
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
@@ -11,9 +11,9 @@ from google.protobuf import symbol_database as _symbol_database
|
|
|
11
11
|
from google.protobuf.internal import builder as _builder
|
|
12
12
|
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
13
|
_runtime_version.Domain.PUBLIC,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
5,
|
|
15
|
+
29,
|
|
16
|
+
5,
|
|
17
17
|
'',
|
|
18
18
|
'seltz_public_api/proto/v1/seltz.proto'
|
|
19
19
|
)
|
|
@@ -24,7 +24,7 @@ _sym_db = _symbol_database.Default()
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%seltz_public_api/proto/v1/seltz.proto\x12\x19seltz_public_api.proto.v1\"F\n\x08Includes\x12(\n\rmax_documents\x18\x01 \x01(\rH\x00R\x0cmaxDocuments\x88\x01\x01\x42\x10\n\x0e_max_documents\"\
|
|
27
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%seltz_public_api/proto/v1/seltz.proto\x12\x19seltz_public_api.proto.v1\"F\n\x08Includes\x12(\n\rmax_documents\x18\x01 \x01(\rH\x00R\x0cmaxDocuments\x88\x01\x01\x42\x10\n\x0e_max_documents\"\xe7\x01\n\rSearchRequest\x12\x14\n\x05query\x18\x01 \x01(\tR\x05query\x12\x44\n\x08includes\x18\x02 \x01(\x0b\x32#.seltz_public_api.proto.v1.IncludesH\x00R\x08includes\x88\x01\x01\x12\x1d\n\x07\x63ontext\x18\x03 \x01(\tH\x01R\x07\x63ontext\x88\x01\x01\x12\x1d\n\x07profile\x18\x04 \x01(\tH\x02R\x07profile\x88\x01\x01\x12\x17\n\x07\x61pi_key\x18\x05 \x01(\tR\x06\x61piKeyB\x0b\n\t_includesB\n\n\x08_contextB\n\n\x08_profile\"T\n\x08\x44ocument\x12\x15\n\x03url\x18\x01 \x01(\tH\x00R\x03url\x88\x01\x01\x12\x1d\n\x07\x63ontent\x18\x02 \x01(\tH\x01R\x07\x63ontent\x88\x01\x01\x42\x06\n\x04_urlB\n\n\x08_content\"S\n\x0eSearchResponse\x12\x41\n\tdocuments\x18\x01 \x03(\x0b\x32#.seltz_public_api.proto.v1.DocumentR\tdocuments2o\n\x0cSeltzService\x12_\n\x06Search\x12(.seltz_public_api.proto.v1.SearchRequest\x1a).seltz_public_api.proto.v1.SearchResponse\"\x00\x62\x06proto3')
|
|
28
28
|
|
|
29
29
|
_globals = globals()
|
|
30
30
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
@@ -34,11 +34,11 @@ if not _descriptor._USE_C_DESCRIPTORS:
|
|
|
34
34
|
_globals['_INCLUDES']._serialized_start=68
|
|
35
35
|
_globals['_INCLUDES']._serialized_end=138
|
|
36
36
|
_globals['_SEARCHREQUEST']._serialized_start=141
|
|
37
|
-
_globals['_SEARCHREQUEST']._serialized_end=
|
|
38
|
-
_globals['_DOCUMENT']._serialized_start=
|
|
39
|
-
_globals['_DOCUMENT']._serialized_end=
|
|
40
|
-
_globals['_SEARCHRESPONSE']._serialized_start=
|
|
41
|
-
_globals['_SEARCHRESPONSE']._serialized_end=
|
|
42
|
-
_globals['_SELTZSERVICE']._serialized_start=
|
|
43
|
-
_globals['_SELTZSERVICE']._serialized_end=
|
|
37
|
+
_globals['_SEARCHREQUEST']._serialized_end=372
|
|
38
|
+
_globals['_DOCUMENT']._serialized_start=374
|
|
39
|
+
_globals['_DOCUMENT']._serialized_end=458
|
|
40
|
+
_globals['_SEARCHRESPONSE']._serialized_start=460
|
|
41
|
+
_globals['_SEARCHRESPONSE']._serialized_end=543
|
|
42
|
+
_globals['_SELTZSERVICE']._serialized_start=545
|
|
43
|
+
_globals['_SELTZSERVICE']._serialized_end=656
|
|
44
44
|
# @@protoc_insertion_point(module_scope)
|
|
@@ -1,35 +1,32 @@
|
|
|
1
|
-
from
|
|
2
|
-
from collections.abc import Mapping as _Mapping
|
|
3
|
-
from typing import ClassVar as _ClassVar
|
|
4
|
-
from typing import Optional as _Optional
|
|
5
|
-
from typing import Union as _Union
|
|
6
|
-
|
|
1
|
+
from google.protobuf.internal import containers as _containers
|
|
7
2
|
from google.protobuf import descriptor as _descriptor
|
|
8
3
|
from google.protobuf import message as _message
|
|
9
|
-
from
|
|
4
|
+
from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union
|
|
10
5
|
|
|
11
6
|
DESCRIPTOR: _descriptor.FileDescriptor
|
|
12
7
|
|
|
13
8
|
class Includes(_message.Message):
|
|
14
|
-
__slots__ = ()
|
|
9
|
+
__slots__ = ("max_documents",)
|
|
15
10
|
MAX_DOCUMENTS_FIELD_NUMBER: _ClassVar[int]
|
|
16
11
|
max_documents: int
|
|
17
12
|
def __init__(self, max_documents: _Optional[int] = ...) -> None: ...
|
|
18
13
|
|
|
19
14
|
class SearchRequest(_message.Message):
|
|
20
|
-
__slots__ = ()
|
|
15
|
+
__slots__ = ("query", "includes", "context", "profile", "api_key")
|
|
21
16
|
QUERY_FIELD_NUMBER: _ClassVar[int]
|
|
22
17
|
INCLUDES_FIELD_NUMBER: _ClassVar[int]
|
|
23
18
|
CONTEXT_FIELD_NUMBER: _ClassVar[int]
|
|
24
19
|
PROFILE_FIELD_NUMBER: _ClassVar[int]
|
|
20
|
+
API_KEY_FIELD_NUMBER: _ClassVar[int]
|
|
25
21
|
query: str
|
|
26
22
|
includes: Includes
|
|
27
23
|
context: str
|
|
28
24
|
profile: str
|
|
29
|
-
|
|
25
|
+
api_key: str
|
|
26
|
+
def __init__(self, query: _Optional[str] = ..., includes: _Optional[_Union[Includes, _Mapping]] = ..., context: _Optional[str] = ..., profile: _Optional[str] = ..., api_key: _Optional[str] = ...) -> None: ...
|
|
30
27
|
|
|
31
28
|
class Document(_message.Message):
|
|
32
|
-
__slots__ = ()
|
|
29
|
+
__slots__ = ("url", "content")
|
|
33
30
|
URL_FIELD_NUMBER: _ClassVar[int]
|
|
34
31
|
CONTENT_FIELD_NUMBER: _ClassVar[int]
|
|
35
32
|
url: str
|
|
@@ -37,7 +34,7 @@ class Document(_message.Message):
|
|
|
37
34
|
def __init__(self, url: _Optional[str] = ..., content: _Optional[str] = ...) -> None: ...
|
|
38
35
|
|
|
39
36
|
class SearchResponse(_message.Message):
|
|
40
|
-
__slots__ = ()
|
|
37
|
+
__slots__ = ("documents",)
|
|
41
38
|
DOCUMENTS_FIELD_NUMBER: _ClassVar[int]
|
|
42
39
|
documents: _containers.RepeatedCompositeFieldContainer[Document]
|
|
43
40
|
def __init__(self, documents: _Optional[_Iterable[_Union[Document, _Mapping]]] = ...) -> None: ...
|
seltz-0.1.2.dist-info/RECORD
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
seltz/__init__.py,sha256=PfEylMpsO-APEQfWbj7nl36ieJVGSb-6RK4501EIpnE,491
|
|
2
|
-
seltz/client.py,sha256=OKdesdOfz84v9zw8oAANC-WwnYrO06zh6ZJIMPnl2cM,1206
|
|
3
|
-
seltz/exceptions.py,sha256=PCFXFY0c5lJuczoB0oWwSMlCAEvVM_HOrU2RR69aZXA,994
|
|
4
|
-
seltz/seltz.py,sha256=ikPqLacdhMwMxILks0GjKIzBZeDB3ePNoAqvJRM4H48,1606
|
|
5
|
-
seltz/services/__init__.py,sha256=hPncxVXXYyEjvBCwe0a6l4KGOKfKuarSONL1MjSekmk,457
|
|
6
|
-
seltz/services/search_service.py,sha256=2t3LzHXWNg_dWHrhSbMBCht0WMz26LpIa5Rgy7kmkRI,2194
|
|
7
|
-
seltz_public_api/proto/v1/seltz_pb2.py,sha256=H5J1GBGixJSdSV_tFIP69x2VMlstAZuvxM2ngikGoHo,2624
|
|
8
|
-
seltz_public_api/proto/v1/seltz_pb2.pyi,sha256=RN_gWIqg7KLndmm7UG6a3BAhDPFZTkNovngPjpgmUlo,1625
|
|
9
|
-
seltz_public_api/proto/v1/seltz_pb2_grpc.py,sha256=Gt-sDKmEsNYzRbzXKvQKIDFoVVnjU8LtEm_sbKyTuro,3024
|
|
10
|
-
seltz-0.1.2.dist-info/METADATA,sha256=kAdyMtPPnvUt_MY6ioQGvGkkRHu1DNx4rln89C5oAH4,3234
|
|
11
|
-
seltz-0.1.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
-
seltz-0.1.2.dist-info/top_level.txt,sha256=omV6Wwfxo_CgGHOAIE8aKEwm2-3c4ETyBCP-0ZIaZs0,23
|
|
13
|
-
seltz-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|