Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0eacfb5
feat: upgrade packaging dep to be compatabile with poetry2.2.1
simonplhak Jan 8, 2026
a80ac1d
refactor: fix formatting
simonplhak Jan 8, 2026
6944c8b
fix: remove doubled method
simonplhak Jan 8, 2026
d82d912
docs: update internal readme
simonplhak Jan 8, 2026
a9d3bc3
fix: finish test for conversation feedback
simonplhak Jan 8, 2026
42256d7
feat: replace requests with httpx
simonplhak Jan 8, 2026
9eda261
feat: move logic to async code and generate sync code automatically
simonplhak Jan 8, 2026
e767e53
feat: add check that code has been ran through generation pipeline in…
simonplhak Jan 8, 2026
8b548c1
docs: add docstring for abstract methods
simonplhak Jan 8, 2026
f79adde
fix: add black formatting to generate_sync script
simonplhak Jan 8, 2026
243bd30
docs: correct interneal docs
simonplhak Jan 12, 2026
f2d29cb
fix: remove manage file
simonplhak Jan 12, 2026
9dc1868
feat: remove generated files and ignore them by git
simonplhak Jan 12, 2026
f4d4c59
fix: remove black formatting from generate_sync script as there is no…
simonplhak Jan 12, 2026
408aa15
feat: include generate sync script in build
simonplhak Jan 12, 2026
7769caf
docs: update internal readme with info about deployment
simonplhak Jan 12, 2026
acfd91b
fix: do not check if generated files are black compatabile as they ar…
simonplhak Jan 12, 2026
d659cd2
fix: be able to import async api clinets directly from kindwise module
simonplhak Jan 12, 2026
1dbc080
fix: await test method in test_crop in async_api tests
simonplhak Jan 12, 2026
1f19256
fix: do not duplicate test for availabel details, keep this test only…
simonplhak Jan 12, 2026
687a9d8
feat: tests for async plant client
simonplhak Jan 12, 2026
471ed60
refactor: do not name unused variable
simonplhak Jan 12, 2026
b6cf29b
docs: correct way to run async code in readme
simonplhak Jan 12, 2026
37977bc
docs: include generate-sync script in setup part
simonplhak Jan 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install poetry==1.3.2
pip install poetry==2.2.1
poetry config virtualenvs.create false
poetry -vv install
- name: generate sync code
run: |
make generate-sync
- name: Lint with black
run: |
black . --check
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@
**/.pytest_cache
dist
**/__pycache__
kindwise/core.py
kindwise/crop_health.py
kindwise/insect.py
kindwise/mushroom.py
kindwise/plant.py
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
generate-sync:
python generate/generate_sync.py

version-patch:
bump2version patch

Expand Down
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ If you want to use the router offline classifier:
pip install kindwise-api-client[router]
```

If you want to use async interface:

```
pip install kindwise-api-client[async]
```

### API key

The API key serves to identify your account and is required to make requests to the API. Get API key at
Expand Down Expand Up @@ -548,3 +554,37 @@ print(router.identify(image_path).simple)
# }

```

### Async Interface

The same available methods are also available in async interface. Here is an example of how to use it.

```python
import asyncio
from kindwise import AsyncPlantApi, PlantIdentification, UsageInfo

async def main():
# initialize plant.id api
# "PLANT_API_KEY" environment variable can be set instead of specifying api_key
api = AsyncPlantApi(api_key='your_api_key')
# get usage information
usage: UsageInfo = await api.usage_info()

# identify plant by image
latitude_longitude = (49.20340, 16.57318)
# pass the image as a path
image_path = 'path/to/plant_image.jpg'
# make identification
identification: PlantIdentification = await api.identify(image_path, latitude_longitude=latitude_longitude)

# get identification by a token with changed views
# this method can be used to modify additional information in identification or to get identification from database
# also works with identification.custom_id
identification_with_different_views: PlantIdentification = await api.get_identification(identification.access_token)

# delete identification
await api.delete_identification(identification) # also works with identification.access_token or identification.custom_id

if __name__ == "__main__":
asyncio.run(main())
```
30 changes: 29 additions & 1 deletion README_internal.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,43 @@
```bash
conda env create
conda activate kindowse-sdk
poetry install
pipx install poetry==2.2.1
poetry install --extras router
pre-commit install
pre-commit install --hook-type commit-msg
make generate-sync
```

## Developmnet

Do not directly modify files under `kindwise` directory. The ground truth code is located in `kindwise/async_api` directory. Use the following command to generate sync code from async code:

```bash
python generate/generate_sync_code.py
# or
make generate-sync
```
## Tests

Specify server used for testing via environmental variable `ENVIRONMENT`.

- LOCAL: usually used for development on one system which is run locally
- STAGING: default
- PRODUCTION


## Deployment

```bash
# Bump version
#
# if changes are not backward compatible, use "major"
make version-major
# if changes are backward compatible, use "minor" and some new features are added
make version-minor
# if only bug fixes, use "patch"
make version-patch

# Publish to PyPI
make publish
```
53 changes: 53 additions & 0 deletions generate/generate_sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import unasync
import os
from pathlib import Path


def post_process(filepath):
"""
Manually fixes imports that unasync's token matching cannot handle.
"""
with open(filepath, "r", encoding="utf-8") as f:
content = f.read()

# FIX 1: Rewrite the dotted import path
# Turns "from kindwise.async_api.core" -> "from kindwise.core"
content = content.replace("kindwise.async_api", "kindwise")

# FIX 2: Ensure any leftover relative imports are correct (if needed)
# content = content.replace("... something else ...", "...")

with open(filepath, "w", encoding="utf-8") as f:
f.write(content)


def main():
gt_code_dir = Path(__file__).resolve().parent.parent / 'kindwise' / 'async_api'
filepaths = [path for path in gt_code_dir.glob('*.py') if path.stem != '__init__']
rules = [
unasync.Rule(
fromdir="/kindwise/async_api",
todir="/kindwise",
additional_replacements={
"AsyncClient": "Client",
"AsyncKindwiseApi": "KindwiseApi",
"anyio": "pathlib",
"AsyncInsectApi": "InsectApi",
"AsyncMushroomApi": "MushroomApi",
"AsyncCropHealthApi": "CropHealthApi",
"AsyncPlantApi": "PlantApi",
},
)
]

filepaths_to_process = [os.path.abspath(p) for p in filepaths]
unasync.unasync_files(filepaths_to_process, rules)
generated_files = []
for path in filepaths:
dest_path = path.parent.parent / path.name
post_process(dest_path)
generated_files.append(str(dest_path))


if __name__ == "__main__":
main()
4 changes: 4 additions & 0 deletions kindwise/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@
from kindwise.mushroom import MushroomApi, MushroomKBType
from kindwise.plant import HealthAssessment, PlantApi, PlantIdentification, PlantKBType, RawPlantIdentification
from kindwise.router import Router, RouterSize
from kindwise.async_api.crop_health import AsyncCropHealthApi
from kindwise.async_api.insect import AsyncInsectApi
from kindwise.async_api.mushroom import AsyncMushroomApi
from kindwise.async_api.plant import AsyncPlantApi
Empty file added kindwise/async_api/__init__.py
Empty file.
Loading