Skip to content

Commit 663a459

Browse files
authored
Merge pull request #63 from JigsawStack/feat/sdk-revamp
SDK Revamp with CI/CD & Bug Fixes
2 parents a846670 + ba56ab1 commit 663a459

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+3433
-1696
lines changed

.github/ruff.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Ruff configuration for CI/CD
2+
line-length = 100
3+
target-version = "py37"
4+
5+
[lint]
6+
select = [
7+
"W", # pycodestyle warnings
8+
"F", # pyflakes
9+
"I", # isort
10+
"B", # flake8-bugbear
11+
"C4", # flake8-comprehensions
12+
"UP", # pyupgrade
13+
]
14+
ignore = [
15+
"B008", # do not perform function calls in argument defaults
16+
]

.github/workflows/ci.yml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main, develop ]
6+
pull_request:
7+
branches: [ main, develop ]
8+
9+
jobs:
10+
ruff-format-check:
11+
name: Ruff Format Check
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Set up Python
17+
uses: actions/setup-python@v4
18+
with:
19+
python-version: '3.12'
20+
21+
- name: Install ruff
22+
run: pip install ruff
23+
24+
- name: Check all files with ruff
25+
run: |
26+
ruff check jigsawstack/ --config .github/ruff.toml
27+
ruff format --check jigsawstack/ --config .github/ruff.toml
28+
29+
test:
30+
name: Test - ${{ matrix.test-file }}
31+
runs-on: ubuntu-latest
32+
strategy:
33+
fail-fast: false
34+
matrix:
35+
test-file:
36+
- test_audio.py
37+
- test_classification.py
38+
- test_embedding.py
39+
- test_file_store.py
40+
- test_image_generation.py
41+
- test_object_detection.py
42+
- test_prediction.py
43+
- test_sentiment.py
44+
- test_sql.py
45+
- test_summary.py
46+
- test_translate.py
47+
- test_validate.py
48+
- test_web.py
49+
- test_deep_research.py
50+
- test_ai_scrape.py
51+
steps:
52+
- uses: actions/checkout@v4
53+
54+
- name: Set up Python
55+
uses: actions/setup-python@v4
56+
with:
57+
python-version: '3.12'
58+
59+
- name: Install dependencies
60+
run: |
61+
python -m pip install --upgrade pip
62+
pip install -r requirements.txt
63+
pip install pytest pytest-asyncio pytest-cov python-dotenv
64+
pip install -e .
65+
66+
- name: Run test ${{ matrix.test-file }}
67+
env:
68+
JIGSAWSTACK_API_KEY: ${{ secrets.JIGSAWSTACK_API_KEY }}
69+
run: |
70+
pytest tests/${{ matrix.test-file }} -v
71+
72+
all-checks-passed:
73+
name: All Checks Passed
74+
needs: [ruff-format-check, test]
75+
runs-on: ubuntu-latest
76+
if: always()
77+
steps:
78+
- name: Verify all checks passed
79+
run: |
80+
echo "Ruff Format Check: ${{ needs.ruff-format-check.result }}"
81+
echo "Tests: ${{ needs.test.result }}"
82+
83+
if [[ "${{ needs.ruff-format-check.result }}" != "success" ]]; then
84+
echo "❌ Ruff format check failed"
85+
exit 1
86+
fi
87+
88+
if [[ "${{ needs.test.result }}" != "success" ]]; then
89+
echo "❌ Tests failed"
90+
exit 1
91+
fi
92+
93+
echo "✅ All checks passed successfully!"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ VOCR:
6868

6969
```py
7070
params = {
71-
"url": "https://rogilvkqloanxtvjfrkm.supabase.co/storage/v1/object/public/demo/Collabo%201080x842.jpg?t=2024-03-22T09%3A22%3A48.442Z"
71+
"url": "https://images.unsplash.com/photo-1542931287-023b922fa89b?q=80&w=2574&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D?t=2024-03-22T09%3A22%3A48.442Z"
7272
}
7373
result = jigsaw.vision.vocr(params)
7474
```

biome.json

Lines changed: 0 additions & 37 deletions
This file was deleted.

jigsawstack/__init__.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
1-
from typing import Union, Dict
21
import os
3-
from .audio import Audio, AsyncAudio
4-
from .vision import Vision, AsyncVision
2+
from typing import Dict, Union
3+
4+
from .audio import AsyncAudio, Audio
5+
from .classification import AsyncClassification, Classification
6+
from .embedding import AsyncEmbedding, Embedding
7+
from .embedding_v2 import AsyncEmbeddingV2, EmbeddingV2
8+
from .exceptions import JigsawStackError
9+
from .image_generation import AsyncImageGeneration, ImageGeneration
10+
from .prediction import AsyncPrediction, Prediction
11+
from .prompt_engine import AsyncPromptEngine, PromptEngine
512
from .search import Search
6-
from .prediction import Prediction, AsyncPrediction
13+
from .sentiment import AsyncSentiment, Sentiment
714
from .sql import SQL, AsyncSQL
8-
from .store import Store, AsyncStore
9-
from .translate import Translate, AsyncTranslate
10-
from .web import Web, AsyncWeb
11-
from .sentiment import Sentiment, AsyncSentiment
12-
from .validate import Validate, AsyncValidate
13-
from .summary import Summary, AsyncSummary
14-
from .embedding import Embedding, AsyncEmbedding
15-
from .exceptions import JigsawStackError
16-
from .image_generation import ImageGeneration, AsyncImageGeneration
17-
from .classification import Classification, AsyncClassification
18-
from .prompt_engine import PromptEngine, AsyncPromptEngine
19-
from .embeddingV2 import EmbeddingV2, AsyncEmbeddingV2
15+
from .store import AsyncStore, Store
16+
from .summary import AsyncSummary, Summary
17+
from .translate import AsyncTranslate, Translate
18+
from .validate import AsyncValidate, Validate
19+
from .vision import AsyncVision, Vision
20+
from .web import AsyncWeb, Web
2021

2122

2223
class JigsawStack:
@@ -51,7 +52,7 @@ def __init__(
5152
if api_url is None:
5253
api_url = os.environ.get("JIGSAWSTACK_API_URL")
5354
if api_url is None:
54-
api_url = f"https://api.jigsawstack.com/"
55+
api_url = "https://api.jigsawstack.com/"
5556

5657
self.api_key = api_key
5758
self.api_url = api_url
@@ -171,7 +172,7 @@ def __init__(
171172
if api_url is None:
172173
api_url = os.environ.get("JIGSAWSTACK_API_URL")
173174
if api_url is None:
174-
api_url = f"https://api.jigsawstack.com/"
175+
api_url = "https://api.jigsawstack.com/"
175176

176177
self.api_key = api_key
177178
self.api_url = api_url

jigsawstack/async_request.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
from typing import Any, Dict, Generic, List, Union, cast, TypedDict, AsyncGenerator
1+
import json
2+
from io import BytesIO
3+
from typing import Any, AsyncGenerator, Dict, Generic, List, TypedDict, Union, cast
4+
25
import aiohttp
36
from typing_extensions import Literal, TypeVar
7+
48
from .exceptions import NoContentError, raise_for_code_and_type
5-
import json
69

710
RequestVerb = Literal["get", "post", "put", "patch", "delete"]
811

@@ -22,7 +25,7 @@ def __init__(
2225
path: str,
2326
params: Union[Dict[Any, Any], List[Dict[Any, Any]]],
2427
verb: RequestVerb,
25-
headers: Dict[str, str] = {"Content-Type": "application/json"},
28+
headers: Dict[str, str] = None,
2629
data: Union[bytes, None] = None,
2730
stream: Union[bool, None] = False,
2831
):
@@ -32,7 +35,7 @@ def __init__(
3235
self.api_url = config.get("api_url")
3336
self.api_key = config.get("api_key")
3437
self.data = data
35-
self.headers = headers
38+
self.headers = headers or {"Content-Type": "application/json"}
3639
self.disable_request_logging = config.get("disable_request_logging")
3740
self.stream = stream
3841

@@ -243,12 +246,27 @@ async def make_request(
243246
)
244247
else:
245248
if data is not None:
249+
form_data = aiohttp.FormData()
250+
form_data.add_field(
251+
"file",
252+
BytesIO(data),
253+
content_type=headers.get("Content-Type", "application/octet-stream"),
254+
filename="file",
255+
)
256+
257+
if self.params and isinstance(self.params, dict):
258+
form_data.add_field(
259+
"body", json.dumps(self.params), content_type="application/json"
260+
)
261+
262+
multipart_headers = headers.copy()
263+
multipart_headers.pop("Content-Type", None)
264+
246265
return await session.request(
247266
verb,
248267
url,
249-
data=data,
250-
params=converted_params, # Use converted params
251-
headers=headers,
268+
data=form_data,
269+
headers=multipart_headers,
252270
)
253271
else:
254272
return await session.request(

jigsawstack/audio.py

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
from typing import Any, Dict, List, cast, Union, Optional, overload
2-
from typing_extensions import NotRequired, TypedDict
3-
from .request import Request, RequestConfig
4-
from .async_request import AsyncRequest, AsyncRequestConfig
1+
from typing import Any, Dict, List, Optional, Union, cast, overload
2+
3+
from typing_extensions import Literal, NotRequired, TypedDict
4+
55
from ._config import ClientConfig
6-
from typing import Any, Dict, List, cast
7-
from typing_extensions import NotRequired, TypedDict, Literal
8-
from .custom_typing import SupportedAccents
9-
from .helpers import build_path
106
from ._types import BaseResponse
7+
from .async_request import AsyncRequest, AsyncRequestConfig
8+
from .request import Request, RequestConfig
119

1210

1311
class SpeechToTextParams(TypedDict):
@@ -80,22 +78,21 @@ def speech_to_text(
8078
blob: Union[SpeechToTextParams, bytes],
8179
options: Optional[SpeechToTextParams] = None,
8280
) -> Union[SpeechToTextResponse, SpeechToTextWebhookResponse]:
81+
options = options or {}
82+
path = "/ai/transcribe"
83+
content_type = options.get("content_type", "application/octet-stream")
84+
headers = {"Content-Type": content_type}
8385
if isinstance(
8486
blob, dict
8587
): # If params is provided as a dict, we assume it's the first argument
8688
resp = Request(
8789
config=self.config,
88-
path="/ai/transcribe",
90+
path=path,
8991
params=cast(Dict[Any, Any], blob),
9092
verb="post",
9193
).perform_with_content()
9294
return resp
9395

94-
options = options or {}
95-
path = build_path(base_path="/ai/transcribe", params=options)
96-
content_type = options.get("content_type", "application/octet-stream")
97-
headers = {"Content-Type": content_type}
98-
9996
resp = Request(
10097
config=self.config,
10198
path=path,
@@ -137,20 +134,19 @@ async def speech_to_text(
137134
blob: Union[SpeechToTextParams, bytes],
138135
options: Optional[SpeechToTextParams] = None,
139136
) -> Union[SpeechToTextResponse, SpeechToTextWebhookResponse]:
137+
options = options or {}
138+
path = "/ai/transcribe"
139+
content_type = options.get("content_type", "application/octet-stream")
140+
headers = {"Content-Type": content_type}
140141
if isinstance(blob, dict):
141142
resp = await AsyncRequest(
142143
config=self.config,
143-
path="/ai/transcribe",
144+
path=path,
144145
params=cast(Dict[Any, Any], blob),
145146
verb="post",
146147
).perform_with_content()
147148
return resp
148149

149-
options = options or {}
150-
path = build_path(base_path="/ai/transcribe", params=options)
151-
content_type = options.get("content_type", "application/octet-stream")
152-
headers = {"Content-Type": content_type}
153-
154150
resp = await AsyncRequest(
155151
config=self.config,
156152
path=path,

jigsawstack/classification.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from typing import Any, Dict, List, Union, cast
2-
from typing_extensions import NotRequired, TypedDict, Literal
3-
from .request import Request, RequestConfig
4-
from .async_request import AsyncRequest, AsyncRequestConfig
2+
3+
from typing_extensions import Literal, NotRequired, TypedDict
4+
55
from ._config import ClientConfig
66
from ._types import BaseResponse
7+
from .async_request import AsyncRequest, AsyncRequestConfig
8+
from .request import Request, RequestConfig
79

810

911
class DatasetItem(TypedDict):

0 commit comments

Comments
 (0)