channel3-sdk 0.0.1__py3-none-any.whl → 0.1.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.

Potentially problematic release.


This version of channel3-sdk might be problematic. Click here for more details.

channel3_sdk/__init__.py CHANGED
@@ -1 +1,44 @@
1
- from .client import Channel3Client
1
+ """Channel3 SDK for Python - Official SDK for the Channel3 AI Shopping API."""
2
+
3
+ from .client import Channel3Client, AsyncChannel3Client
4
+ from .models import (
5
+ Product,
6
+ ProductDetail,
7
+ SearchFilters,
8
+ SearchRequest,
9
+ MerchantOffering,
10
+ FamilyMember,
11
+ Price,
12
+ AvailabilityStatus,
13
+ )
14
+ from .exceptions import (
15
+ Channel3Error,
16
+ Channel3AuthenticationError,
17
+ Channel3ValidationError,
18
+ Channel3NotFoundError,
19
+ Channel3ServerError,
20
+ Channel3ConnectionError,
21
+ )
22
+
23
+ __version__ = "0.1.0"
24
+ __all__ = [
25
+ # Clients
26
+ "Channel3Client",
27
+ "AsyncChannel3Client",
28
+ # Models
29
+ "Product",
30
+ "ProductDetail",
31
+ "SearchFilters",
32
+ "SearchRequest",
33
+ "MerchantOffering",
34
+ "FamilyMember",
35
+ "Price",
36
+ "AvailabilityStatus",
37
+ # Exceptions
38
+ "Channel3Error",
39
+ "Channel3AuthenticationError",
40
+ "Channel3ValidationError",
41
+ "Channel3NotFoundError",
42
+ "Channel3ServerError",
43
+ "Channel3ConnectionError",
44
+ ]
channel3_sdk/client.py CHANGED
@@ -2,46 +2,93 @@
2
2
  import os
3
3
  import requests
4
4
  from typing import List, Optional, Dict, Any, Union
5
+ import aiohttp
6
+ import asyncio
7
+ from pydantic import ValidationError
5
8
 
9
+ from .models import Product, ProductDetail, SearchFilters, SearchRequest
10
+ from .exceptions import (
11
+ Channel3Error,
12
+ Channel3AuthenticationError,
13
+ Channel3ValidationError,
14
+ Channel3NotFoundError,
15
+ Channel3ServerError,
16
+ Channel3ConnectionError,
17
+ )
6
18
 
7
- class Channel3Client:
8
- def __init__(self, api_key: str = None):
9
- """
10
- Initialize a Channel3 API client.
11
19
 
12
- The client provides methods to interact with Channel3's product search and retrieval API.
20
+ class BaseChannel3Client:
21
+ """Base client with common functionality."""
22
+
23
+ def __init__(self, api_key: Optional[str] = None, base_url: Optional[str] = None):
24
+ """
25
+ Initialize the base Channel3 client.
13
26
 
14
27
  Args:
15
28
  api_key: Your Channel3 API key. If not provided, will look for CHANNEL3_API_KEY in environment.
29
+ base_url: Base URL for the API. Defaults to https://api.trychannel3.com/v0
16
30
 
17
31
  Raises:
18
32
  ValueError: If no API key is provided and none is found in environment variables.
19
-
20
- Example:
21
- ```python
22
- # Initialize with explicit API key
23
- client = Channel3Client(api_key="your_api_key")
24
-
25
- # Or use environment variable
26
- # os.environ["CHANNEL3_API_KEY"] = "your_api_key"
27
- # client = Channel3Client()
28
- ```
29
33
  """
30
34
  self.api_key = api_key or os.getenv("CHANNEL3_API_KEY")
31
35
  if not self.api_key:
32
- raise ValueError("No API key provided for Channel3Client")
33
- self.headers = {"x-api-key": self.api_key}
34
- self.api_version = "v0"
35
- self.base_url = f"https://api.channel3.com/{self.api_version}"
36
+ raise ValueError(
37
+ "No API key provided. Set CHANNEL3_API_KEY environment variable or pass api_key parameter."
38
+ )
39
+
40
+ self.base_url = base_url or "https://api.trychannel3.com/v0"
41
+ self.headers = {"x-api-key": self.api_key, "Content-Type": "application/json"}
42
+
43
+ def _handle_error_response(
44
+ self, status_code: int, response_data: Dict[str, Any], url: str
45
+ ) -> None:
46
+ """Handle error responses and raise appropriate exceptions."""
47
+ error_message = response_data.get(
48
+ "detail", f"Request failed with status {status_code}"
49
+ )
50
+
51
+ if status_code == 401:
52
+ raise Channel3AuthenticationError(
53
+ "Invalid or missing API key",
54
+ status_code=status_code,
55
+ response_data=response_data,
56
+ )
57
+ elif status_code == 404:
58
+ raise Channel3NotFoundError(
59
+ error_message, status_code=status_code, response_data=response_data
60
+ )
61
+ elif status_code == 422:
62
+ raise Channel3ValidationError(
63
+ f"Validation error: {error_message}",
64
+ status_code=status_code,
65
+ response_data=response_data,
66
+ )
67
+ elif status_code == 500:
68
+ raise Channel3ServerError(
69
+ "Internal server error",
70
+ status_code=status_code,
71
+ response_data=response_data,
72
+ )
73
+ else:
74
+ raise Channel3Error(
75
+ f"Request to {url} failed: {error_message}",
76
+ status_code=status_code,
77
+ response_data=response_data,
78
+ )
79
+
80
+
81
+ class Channel3Client(BaseChannel3Client):
82
+ """Synchronous Channel3 API client."""
36
83
 
37
84
  def search(
38
85
  self,
39
86
  query: Optional[str] = None,
40
87
  image_url: Optional[str] = None,
41
88
  base64_image: Optional[str] = None,
42
- filters: Optional[Dict[str, Any]] = None,
89
+ filters: Optional[Union[SearchFilters, Dict[str, Any]]] = None,
43
90
  limit: int = 20,
44
- ) -> List[Dict[str, Any]]:
91
+ ) -> List[Product]:
45
92
  """
46
93
  Search for products using text query, image, or both with optional filters.
47
94
 
@@ -49,16 +96,17 @@ class Channel3Client:
49
96
  query: Text search query
50
97
  image_url: URL to an image to use for visual search
51
98
  base64_image: Base64-encoded image to use for visual search
52
- filters: Dict containing optional filters with these possible keys:
53
- - colors: List of color strings
54
- - materials: List of material strings
55
- - min_price: Minimum price (float)
56
- - max_price: Maximum price (float)
99
+ filters: Search filters (SearchFilters object or dict)
57
100
  limit: Maximum number of products to return (default: 20)
58
101
 
59
102
  Returns:
60
- List of product dictionaries containing:
61
- id, url, score, price, brand_id, brand_name, title, description, image_url, variants
103
+ List of Product objects
104
+
105
+ Raises:
106
+ Channel3AuthenticationError: If API key is invalid
107
+ Channel3ValidationError: If request parameters are invalid
108
+ Channel3ServerError: If server encounters an error
109
+ Channel3ConnectionError: If there are connection issues
62
110
 
63
111
  Examples:
64
112
  ```python
@@ -68,43 +116,179 @@ class Channel3Client:
68
116
  # Image search
69
117
  products = client.search(image_url="https://example.com/image.jpg")
70
118
 
71
- # Multimodal search
72
- products = client.search(
73
- query="blue denim jacket",
74
- base64_image="data:image/jpeg;base64,...",
119
+ # Multimodal search with filters
120
+ from channel3_sdk.models import SearchFilters
121
+ filters = SearchFilters(colors=["blue"], min_price=50.0, max_price=150.0)
122
+ products = client.search(query="denim jacket", filters=filters)
123
+ ```
124
+ """
125
+ # Build request payload
126
+ search_request = SearchRequest(
127
+ query=query,
128
+ image_url=image_url,
129
+ base64_image=base64_image,
130
+ filters=filters,
131
+ limit=limit,
132
+ )
133
+
134
+ url = f"{self.base_url}/search"
135
+
136
+ try:
137
+ response = requests.post(
138
+ url,
139
+ json=search_request.model_dump(exclude_none=True),
140
+ headers=self.headers,
141
+ timeout=30,
142
+ )
143
+
144
+ response_data = response.json()
145
+
146
+ if response.status_code != 200:
147
+ self._handle_error_response(response.status_code, response_data, url)
148
+
149
+ # Parse and validate response
150
+ return [Product(**item) for item in response_data]
151
+
152
+ except requests.exceptions.ConnectionError as e:
153
+ raise Channel3ConnectionError(
154
+ f"Failed to connect to Channel3 API: {str(e)}"
75
155
  )
156
+ except requests.exceptions.Timeout as e:
157
+ raise Channel3ConnectionError(f"Request timed out: {str(e)}")
158
+ except requests.exceptions.RequestException as e:
159
+ raise Channel3Error(f"Request failed: {str(e)}")
160
+ except ValidationError as e:
161
+ raise Channel3Error(f"Invalid response format: {str(e)}")
162
+
163
+ def get_product(self, product_id: str) -> ProductDetail:
164
+ """
165
+ Get detailed information about a specific product by its ID.
166
+
167
+ Args:
168
+ product_id: The unique identifier of the product
169
+
170
+ Returns:
171
+ ProductDetail object with detailed product information
172
+
173
+ Raises:
174
+ Channel3AuthenticationError: If API key is invalid
175
+ Channel3NotFoundError: If product is not found
176
+ Channel3ValidationError: If product_id is invalid
177
+ Channel3ServerError: If server encounters an error
178
+ Channel3ConnectionError: If there are connection issues
179
+
180
+ Example:
181
+ ```python
182
+ product_detail = client.get_product("prod_123456")
183
+ print(f"Product: {product_detail.title}")
184
+ print(f"Brand: {product_detail.brand_name}")
185
+ ```
186
+ """
187
+ if not product_id or not product_id.strip():
188
+ raise ValueError("product_id cannot be empty")
189
+
190
+ url = f"{self.base_url}/products/{product_id}"
191
+
192
+ try:
193
+ response = requests.get(url, headers=self.headers, timeout=30)
194
+ response_data = response.json()
195
+
196
+ if response.status_code != 200:
197
+ self._handle_error_response(response.status_code, response_data, url)
198
+
199
+ # Parse and validate response
200
+ return ProductDetail(**response_data)
76
201
 
77
- # Search with filters
78
- products = client.search(
79
- query="running shoes",
80
- filters={
81
- "colors": ["black", "white"],
82
- "min_price": 50.0,
83
- "max_price": 150.0
84
- },
85
- limit=10
202
+ except requests.exceptions.ConnectionError as e:
203
+ raise Channel3ConnectionError(
204
+ f"Failed to connect to Channel3 API: {str(e)}"
86
205
  )
206
+ except requests.exceptions.Timeout as e:
207
+ raise Channel3ConnectionError(f"Request timed out: {str(e)}")
208
+ except requests.exceptions.RequestException as e:
209
+ raise Channel3Error(f"Request failed: {str(e)}")
210
+ except ValidationError as e:
211
+ raise Channel3Error(f"Invalid response format: {str(e)}")
212
+
213
+
214
+ class AsyncChannel3Client(BaseChannel3Client):
215
+ """Asynchronous Channel3 API client."""
216
+
217
+ async def search(
218
+ self,
219
+ query: Optional[str] = None,
220
+ image_url: Optional[str] = None,
221
+ base64_image: Optional[str] = None,
222
+ filters: Optional[Union[SearchFilters, Dict[str, Any]]] = None,
223
+ limit: int = 20,
224
+ ) -> List[Product]:
225
+ """
226
+ Search for products using text query, image, or both with optional filters.
227
+
228
+ Args:
229
+ query: Text search query
230
+ image_url: URL to an image to use for visual search
231
+ base64_image: Base64-encoded image to use for visual search
232
+ filters: Search filters (SearchFilters object or dict)
233
+ limit: Maximum number of products to return (default: 20)
234
+
235
+ Returns:
236
+ List of Product objects
237
+
238
+ Raises:
239
+ Channel3AuthenticationError: If API key is invalid
240
+ Channel3ValidationError: If request parameters are invalid
241
+ Channel3ServerError: If server encounters an error
242
+ Channel3ConnectionError: If there are connection issues
243
+
244
+ Examples:
245
+ ```python
246
+ # Text search
247
+ products = await async_client.search(query="blue denim jacket")
248
+
249
+ # Image search
250
+ products = await async_client.search(image_url="https://example.com/image.jpg")
87
251
  ```
88
252
  """
89
- payload = {
90
- "query": query,
91
- "image_url": image_url,
92
- "base64_image": base64_image,
93
- "limit": limit,
94
- }
95
-
96
- if filters:
97
- payload["filters"] = filters
98
-
99
- response = requests.post(
100
- f"{self.base_url}/search",
101
- json={k: v for k, v in payload.items() if v is not None},
102
- headers=self.headers,
253
+ # Build request payload
254
+ search_request = SearchRequest(
255
+ query=query,
256
+ image_url=image_url,
257
+ base64_image=base64_image,
258
+ filters=filters,
259
+ limit=limit,
103
260
  )
104
- response.raise_for_status()
105
- return response.json()
106
261
 
107
- def get_product(self, product_id: str) -> Dict[str, Any]:
262
+ url = f"{self.base_url}/search"
263
+
264
+ try:
265
+ async with aiohttp.ClientSession() as session:
266
+ async with session.post(
267
+ url,
268
+ json=search_request.model_dump(exclude_none=True),
269
+ headers=self.headers,
270
+ timeout=aiohttp.ClientTimeout(total=30),
271
+ ) as response:
272
+ response_data = await response.json()
273
+
274
+ if response.status != 200:
275
+ self._handle_error_response(response.status, response_data, url)
276
+
277
+ # Parse and validate response
278
+ return [Product(**item) for item in response_data]
279
+
280
+ except aiohttp.ClientConnectionError as e:
281
+ raise Channel3ConnectionError(
282
+ f"Failed to connect to Channel3 API: {str(e)}"
283
+ )
284
+ except asyncio.TimeoutError as e:
285
+ raise Channel3ConnectionError(f"Request timed out: {str(e)}")
286
+ except aiohttp.ClientError as e:
287
+ raise Channel3Error(f"Request failed: {str(e)}")
288
+ except ValidationError as e:
289
+ raise Channel3Error(f"Invalid response format: {str(e)}")
290
+
291
+ async def get_product(self, product_id: str) -> ProductDetail:
108
292
  """
109
293
  Get detailed information about a specific product by its ID.
110
294
 
@@ -112,23 +296,46 @@ class Channel3Client:
112
296
  product_id: The unique identifier of the product
113
297
 
114
298
  Returns:
115
- Dictionary containing detailed product information:
116
- url, brand_id, brand_name, title, description, image_urls, variants,
117
- price, gender, materials, key_features
299
+ ProductDetail object with detailed product information
118
300
 
119
301
  Raises:
120
- requests.HTTPError: If the product does not exist or other API errors occur
302
+ Channel3AuthenticationError: If API key is invalid
303
+ Channel3NotFoundError: If product is not found
304
+ Channel3ValidationError: If product_id is invalid
305
+ Channel3ServerError: If server encounters an error
306
+ Channel3ConnectionError: If there are connection issues
121
307
 
122
308
  Example:
123
309
  ```python
124
- product_detail = client.get_product("prod_123456")
125
- print(product_detail["title"])
126
- print(product_detail["price"]["price"])
310
+ product_detail = await async_client.get_product("prod_123456")
311
+ print(f"Product: {product_detail.title}")
127
312
  ```
128
313
  """
129
- response = requests.get(
130
- f"{self.base_url}/products/{product_id}",
131
- headers=self.headers,
132
- )
133
- response.raise_for_status()
134
- return response.json()
314
+ if not product_id or not product_id.strip():
315
+ raise ValueError("product_id cannot be empty")
316
+
317
+ url = f"{self.base_url}/products/{product_id}"
318
+
319
+ try:
320
+ async with aiohttp.ClientSession() as session:
321
+ async with session.get(
322
+ url, headers=self.headers, timeout=aiohttp.ClientTimeout(total=30)
323
+ ) as response:
324
+ response_data = await response.json()
325
+
326
+ if response.status != 200:
327
+ self._handle_error_response(response.status, response_data, url)
328
+
329
+ # Parse and validate response
330
+ return ProductDetail(**response_data)
331
+
332
+ except aiohttp.ClientConnectionError as e:
333
+ raise Channel3ConnectionError(
334
+ f"Failed to connect to Channel3 API: {str(e)}"
335
+ )
336
+ except asyncio.TimeoutError as e:
337
+ raise Channel3ConnectionError(f"Request timed out: {str(e)}")
338
+ except aiohttp.ClientError as e:
339
+ raise Channel3Error(f"Request failed: {str(e)}")
340
+ except ValidationError as e:
341
+ raise Channel3Error(f"Invalid response format: {str(e)}")
@@ -0,0 +1,48 @@
1
+ """Custom exceptions for the Channel3 SDK."""
2
+
3
+ from typing import Optional, Dict, Any
4
+
5
+
6
+ class Channel3Error(Exception):
7
+ """Base exception for all Channel3 SDK errors."""
8
+
9
+ def __init__(
10
+ self,
11
+ message: str,
12
+ status_code: Optional[int] = None,
13
+ response_data: Optional[Dict[str, Any]] = None,
14
+ ):
15
+ super().__init__(message)
16
+ self.message = message
17
+ self.status_code = status_code
18
+ self.response_data = response_data or {}
19
+
20
+
21
+ class Channel3AuthenticationError(Channel3Error):
22
+ """Raised when authentication fails (401)."""
23
+
24
+ pass
25
+
26
+
27
+ class Channel3ValidationError(Channel3Error):
28
+ """Raised when request validation fails (422)."""
29
+
30
+ pass
31
+
32
+
33
+ class Channel3NotFoundError(Channel3Error):
34
+ """Raised when a resource is not found (404)."""
35
+
36
+ pass
37
+
38
+
39
+ class Channel3ServerError(Channel3Error):
40
+ """Raised when the server encounters an error (500)."""
41
+
42
+ pass
43
+
44
+
45
+ class Channel3ConnectionError(Channel3Error):
46
+ """Raised when there are connection issues."""
47
+
48
+ pass
channel3_sdk/models.py ADDED
@@ -0,0 +1,116 @@
1
+ """Pydantic models for the Channel3 API."""
2
+
3
+ from enum import Enum
4
+ from typing import List, Optional, Union, Literal
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class AvailabilityStatus(str, Enum):
9
+ """Availability status of a product."""
10
+
11
+ IN_STOCK = "InStock"
12
+ OUT_OF_STOCK = "OutOfStock"
13
+ PRE_ORDER = "PreOrder"
14
+ LIMITED_AVAILABILITY = "LimitedAvailability"
15
+ BACK_ORDER = "BackOrder"
16
+ DISCONTINUED = "Discontinued"
17
+ SOLD_OUT = "SoldOut"
18
+ UNKNOWN = "Unknown"
19
+
20
+
21
+ class Price(BaseModel):
22
+ """Price information for a product."""
23
+
24
+ price: float = Field(
25
+ ..., description="The current price of the product, including any discounts."
26
+ )
27
+ compare_at_price: Optional[float] = Field(
28
+ None, description="The original price of the product before any discounts."
29
+ )
30
+ currency: str = Field(..., description="The currency code of the product.")
31
+
32
+
33
+ class MerchantOffering(BaseModel):
34
+ """A merchant offering a product."""
35
+
36
+ url: str = Field(
37
+ default="https://buy.trychannel3.com", description="URL to purchase the product"
38
+ )
39
+ merchant_name: str = Field(..., description="Name of the merchant")
40
+ price: Price = Field(..., description="Price information")
41
+ availability: AvailabilityStatus = Field(
42
+ ..., description="Product availability status"
43
+ )
44
+
45
+
46
+ class FamilyMember(BaseModel):
47
+ """A family member product."""
48
+
49
+ id: str = Field(..., description="Unique identifier for the family member")
50
+ title: str = Field(..., description="Title of the family member product")
51
+ image_url: str = Field(..., description="Image URL for the family member product")
52
+
53
+
54
+ class Product(BaseModel):
55
+ """A product returned from search."""
56
+
57
+ id: str = Field(..., description="Unique identifier for the product")
58
+ score: float = Field(..., description="Relevance score for the search query")
59
+ brand_name: str = Field(..., description="Brand name of the product")
60
+ title: str = Field(..., description="Product title")
61
+ description: str = Field(..., description="Product description")
62
+ image_url: str = Field(..., description="Main product image URL")
63
+ offers: List[MerchantOffering] = Field(
64
+ ..., description="List of merchant offerings"
65
+ )
66
+ family: List[FamilyMember] = Field(
67
+ default_factory=list, description="Related family products"
68
+ )
69
+
70
+
71
+ class ProductDetail(BaseModel):
72
+ """Detailed information about a product."""
73
+
74
+ brand_id: str = Field(..., description="Unique identifier for the brand")
75
+ brand_name: str = Field(..., description="Brand name of the product")
76
+ title: str = Field(..., description="Product title")
77
+ description: str = Field(..., description="Product description")
78
+ image_urls: List[str] = Field(..., description="List of product image URLs")
79
+ merchant_offerings: List[MerchantOffering] = Field(
80
+ ..., description="List of merchant offerings"
81
+ )
82
+ gender: Literal["na", "men", "women"] = Field(
83
+ default="na", description="Target gender"
84
+ )
85
+ materials: Optional[List[str]] = Field(None, description="List of materials")
86
+ key_features: List[str] = Field(
87
+ default_factory=list, description="List of key product features"
88
+ )
89
+ family_members: List[FamilyMember] = Field(
90
+ default_factory=list, description="Related family products"
91
+ )
92
+
93
+
94
+ class SearchFilters(BaseModel):
95
+ """Search filters for product search."""
96
+
97
+ colors: Optional[List[str]] = Field(None, description="List of colors to filter by")
98
+ materials: Optional[List[str]] = Field(
99
+ None, description="List of materials to filter by"
100
+ )
101
+ min_price: Optional[float] = Field(None, description="Minimum price filter")
102
+ max_price: Optional[float] = Field(None, description="Maximum price filter")
103
+
104
+
105
+ class SearchRequest(BaseModel):
106
+ """Request model for product search."""
107
+
108
+ query: Optional[str] = Field(None, description="Text search query")
109
+ image_url: Optional[str] = Field(None, description="URL of image for visual search")
110
+ base64_image: Optional[str] = Field(
111
+ None, description="Base64-encoded image for visual search"
112
+ )
113
+ filters: Optional[SearchFilters] = Field(None, description="Search filters")
114
+ limit: Optional[int] = Field(
115
+ default=20, description="Maximum number of results to return"
116
+ )
@@ -0,0 +1,235 @@
1
+ Metadata-Version: 2.1
2
+ Name: channel3-sdk
3
+ Version: 0.1.0
4
+ Summary: The official Python SDK for Channel3 AI Shopping API
5
+ Home-page: https://github.com/channel3/sdk-python
6
+ License: MIT
7
+ Keywords: ai,shopping,ecommerce,search,api
8
+ Author: Channel3
9
+ Author-email: alex@trychannel3.com
10
+ Requires-Python: >=3.8,<4.0
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Requires-Dist: aiohttp (>=3.9.0,<4.0.0)
20
+ Requires-Dist: pydantic (>=2.5.0,<3.0.0)
21
+ Requires-Dist: requests (>=2.31.0,<3.0.0)
22
+ Project-URL: Repository, https://github.com/channel3/sdk-python
23
+ Description-Content-Type: text/markdown
24
+
25
+ # Channel3 Python SDK
26
+
27
+ The official Python SDK for the [Channel3](https://trychannel3.com) AI Shopping API.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install channel3-sdk
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ### Synchronous Client
38
+
39
+ ```python
40
+ import os
41
+ from channel3_sdk import Channel3Client
42
+
43
+ # Initialize the client
44
+ client = Channel3Client(api_key="your_api_key_here")
45
+ # Or use environment variable: CHANNEL3_API_KEY
46
+
47
+ # Search for products
48
+ products = client.search(query="blue denim jacket")
49
+
50
+ for product in products:
51
+ print(f"Product: {product.title}")
52
+ print(f"Brand: {product.brand_name}")
53
+ print(f"Price: ${product.offers[0].price.price}")
54
+ print("---")
55
+
56
+ # Get detailed product information
57
+ product_detail = client.get_product("prod_123456")
58
+ print(f"Detailed info for: {product_detail.title}")
59
+ print(f"Materials: {product_detail.materials}")
60
+ print(f"Key features: {product_detail.key_features}")
61
+ ```
62
+
63
+ ### Asynchronous Client
64
+
65
+ ```python
66
+ import asyncio
67
+ from channel3_sdk import AsyncChannel3Client
68
+
69
+ async def main():
70
+ # Initialize the async client
71
+ client = AsyncChannel3Client(api_key="your_api_key_here")
72
+
73
+ # Search for products
74
+ products = await client.search(query="running shoes")
75
+
76
+ for product in products:
77
+ print(f"Product: {product.title}")
78
+ print(f"Score: {product.score}")
79
+
80
+ # Get detailed product information
81
+ if products:
82
+ product_detail = await client.get_product(products[0].id)
83
+ print(f"Gender: {product_detail.gender}")
84
+
85
+ # Run the async function
86
+ asyncio.run(main())
87
+ ```
88
+
89
+ ## Advanced Usage
90
+
91
+ ### Visual Search
92
+
93
+ ```python
94
+ # Search by image URL
95
+ products = client.search(image_url="https://example.com/image.jpg")
96
+
97
+ # Search by base64 image
98
+ with open("image.jpg", "rb") as f:
99
+ import base64
100
+ base64_image = base64.b64encode(f.read()).decode()
101
+ products = client.search(base64_image=base64_image)
102
+ ```
103
+
104
+ ### Multimodal Search
105
+
106
+ ```python
107
+ # Combine text and image search
108
+ products = client.search(
109
+ query="blue denim jacket",
110
+ image_url="https://example.com/jacket.jpg"
111
+ )
112
+ ```
113
+
114
+ ### Search with Filters
115
+
116
+ ```python
117
+ from channel3_sdk import SearchFilters
118
+
119
+ # Create search filters
120
+ filters = SearchFilters(
121
+ colors=["blue", "navy"],
122
+ materials=["cotton", "denim"],
123
+ min_price=50.0,
124
+ max_price=200.0
125
+ )
126
+
127
+ # Search with filters
128
+ products = client.search(
129
+ query="jacket",
130
+ filters=filters,
131
+ limit=10
132
+ )
133
+ ```
134
+
135
+ ## API Reference
136
+
137
+ ### Client Classes
138
+
139
+ #### `Channel3Client`
140
+ Synchronous client for the Channel3 API.
141
+
142
+ **Methods:**
143
+ - `search(query=None, image_url=None, base64_image=None, filters=None, limit=20)` → `List[Product]`
144
+ - `get_product(product_id)` → `ProductDetail`
145
+
146
+ #### `AsyncChannel3Client`
147
+ Asynchronous client for the Channel3 API.
148
+
149
+ **Methods:**
150
+ - `async search(query=None, image_url=None, base64_image=None, filters=None, limit=20)` → `List[Product]`
151
+ - `async get_product(product_id)` → `ProductDetail`
152
+
153
+ ### Models
154
+
155
+ #### `Product`
156
+ - `id: str` - Unique product identifier
157
+ - `score: float` - Search relevance score
158
+ - `brand_name: str` - Brand name
159
+ - `title: str` - Product title
160
+ - `description: str` - Product description
161
+ - `image_url: str` - Main product image URL
162
+ - `offers: List[MerchantOffering]` - Available purchase options
163
+ - `family: List[FamilyMember]` - Related products
164
+
165
+ #### `ProductDetail`
166
+ - `brand_id: str` - Brand identifier
167
+ - `brand_name: str` - Brand name
168
+ - `title: str` - Product title
169
+ - `description: str` - Product description
170
+ - `image_urls: List[str]` - Product image URLs
171
+ - `merchant_offerings: List[MerchantOffering]` - Purchase options
172
+ - `gender: Literal["na", "men", "women"]` - Target gender
173
+ - `materials: Optional[List[str]]` - Product materials
174
+ - `key_features: List[str]` - Key product features
175
+ - `family_members: List[FamilyMember]` - Related products
176
+
177
+ #### `SearchFilters`
178
+ - `colors: Optional[List[str]]` - Color filters
179
+ - `materials: Optional[List[str]]` - Material filters
180
+ - `min_price: Optional[float]` - Minimum price
181
+ - `max_price: Optional[float]` - Maximum price
182
+
183
+ #### `MerchantOffering`
184
+ - `url: str` - Purchase URL
185
+ - `merchant_name: str` - Merchant name
186
+ - `price: Price` - Price information
187
+ - `availability: AvailabilityStatus` - Availability status
188
+
189
+ #### `Price`
190
+ - `price: float` - Current price
191
+ - `compare_at_price: Optional[float]` - Original price (if discounted)
192
+ - `currency: str` - Currency code
193
+
194
+ ## Error Handling
195
+
196
+ The SDK provides specific exception types for different error conditions:
197
+
198
+ ```python
199
+ from channel3_sdk import (
200
+ Channel3AuthenticationError,
201
+ Channel3ValidationError,
202
+ Channel3NotFoundError,
203
+ Channel3ServerError,
204
+ Channel3ConnectionError
205
+ )
206
+
207
+ try:
208
+ products = client.search(query="shoes")
209
+ except Channel3AuthenticationError:
210
+ print("Invalid API key")
211
+ except Channel3ValidationError as e:
212
+ print(f"Invalid request: {e.message}")
213
+ except Channel3NotFoundError:
214
+ print("Resource not found")
215
+ except Channel3ServerError:
216
+ print("Server error - please try again later")
217
+ except Channel3ConnectionError:
218
+ print("Connection error - check your internet connection")
219
+ ```
220
+
221
+ ## Environment Variables
222
+
223
+ - `CHANNEL3_API_KEY` - Your Channel3 API key
224
+
225
+ ## Requirements
226
+
227
+ - Python 3.8+
228
+ - requests
229
+ - aiohttp
230
+ - pydantic
231
+
232
+ ## License
233
+
234
+ MIT License
235
+
@@ -0,0 +1,7 @@
1
+ channel3_sdk/__init__.py,sha256=aoe0MhblA5pEOd7iAggJ94u1Os8-xCBs6k0UWhfOiiE,960
2
+ channel3_sdk/client.py,sha256=JuJFtXdWsezwOQk3nVUnxhdtQp300UEORsQZdCZ77V8,12525
3
+ channel3_sdk/exceptions.py,sha256=5HJgrhir4-_b4XKyWAB9qp-CjVZeigXSkyM9fbcB1IA,1044
4
+ channel3_sdk/models.py,sha256=AMgcsCK-jnaKA9iiL0CZvWJMprA1AxspIu4IM6vq-WE,4326
5
+ channel3_sdk-0.1.0.dist-info/METADATA,sha256=xGgwGQ8iQTs1u7zeYK22RSF-EW02fYvPlqeC-4xerqs,6137
6
+ channel3_sdk-0.1.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
7
+ channel3_sdk-0.1.0.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: channel3-sdk
3
- Version: 0.0.1
4
- Summary: The API for AI Shopping
5
- License: MIT
6
- Author: Alex
7
- Author-email: alex@trychannel3.com
8
- Requires-Python: >=3.10,<4.0
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.10
12
- Classifier: Programming Language :: Python :: 3.11
13
- Classifier: Programming Language :: Python :: 3.12
14
- Classifier: Programming Language :: Python :: 3.13
15
- Requires-Dist: requests (>=2.31.0,<3.0.0)
16
- Description-Content-Type: text/markdown
17
-
18
-
@@ -1,5 +0,0 @@
1
- channel3_sdk/__init__.py,sha256=RTKSE_cJNdqeRdvb2ezu7o_Jje08I9zWE2p7w5OAk9s,35
2
- channel3_sdk/client.py,sha256=UYTPzAf2HmJOLPi3lAdrL5GmO359zY-NkuWgbeo_ass,4496
3
- channel3_sdk-0.0.1.dist-info/METADATA,sha256=zvI-DgEYvUWF-GHC0vSqGoevJm7KYHtzcNu-_GDnTsA,566
4
- channel3_sdk-0.0.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
5
- channel3_sdk-0.0.1.dist-info/RECORD,,