Skip to content

Commit

Permalink
Merge branch 'master' into grpcio-1.64
Browse files Browse the repository at this point in the history
  • Loading branch information
JoanFM authored Jul 10, 2024
2 parents b34c2b7 + e3ea29f commit df06a19
Show file tree
Hide file tree
Showing 16 changed files with 313 additions and 29 deletions.
8 changes: 1 addition & 7 deletions .github/workflows/force-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,7 @@ jobs:
if: ${{ matrix.os == 'macos-latest' }}
run: |
python -m cibuildwheel --output-dir dist
- name: Test wheels
run: |
WHEEL_FILE=$(ls dist/*.whl)
python -m pip install $WHEEL_FILE
python -c "import jraft"
if: ${{ matrix.os != 'macos-latest' || matrix.platform_id != 'macosx_arm64' }} # runners do not necessarily have macos ARM, so cannot run this test for it

- name: Upload wheels as artifacts
uses: actions/upload-artifact@v2
with:
Expand Down
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@






# Change Logs
Expand Down Expand Up @@ -668,6 +669,7 @@ Jina is released on every Friday evening. The PyPi package and Docker Image will
- [Release Note (`3.24.1`)](#release-note-3241)
- [Release Note (`3.25.0`)](#release-note-3250)
- [Release Note (`3.25.1`)](#release-note-3251)
- [Release Note (`3.25.2`)](#release-note-3252)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

Expand Down Expand Up @@ -16640,3 +16642,29 @@ Jina is released on every Friday evening. The PyPi package and Docker Image will
- [[```49461515```](https://github.com/jina-ai/jina/commit/49461515622b6addc66e15f6da922f01451fb93e)] __-__ __docs__: update TOC (*Jina Dev Bot*)
- [[```8c9df630```](https://github.com/jina-ai/jina/commit/8c9df6306e71aaae98a82b890c1b820a72ecd361)] __-__ __version__: the next version will be 3.25.1 (*Jina Dev Bot*)

<a name=release-note-3-25-2></a>
## Release Note (`3.25.2`)

> Release time: 2024-06-13 16:32:38



🙇 We'd like to thank all contributors for this new release! In particular,
Joan Fontanals, Zac Li, Jina Dev Bot, 🙇


### 🐞 Bug fixes

- [[```ae142c00```](https://github.com/jina-ai/jina/commit/ae142c00e582db021e6dcd12c8ec645fe3115074)] __-__ update force-release.yml (#6172) (*Joan Fontanals*)
- [[```98429b05```](https://github.com/jina-ai/jina/commit/98429b055a8e2f4cfc97819cdf516ada2a23725d)] __-__ support sagemaker batch transform for clip (#6171) (*Zac Li*)

### 🏁 Unit Test and CICD

- [[```7e2247d2```](https://github.com/jina-ai/jina/commit/7e2247d277ac9169863c5ad10606c6605cdc7ed3)] __-__ update force-release.yml (#6173) (*Joan Fontanals*)
- [[```12e2a945```](https://github.com/jina-ai/jina/commit/12e2a945ec1c9b1090597163f94092b9d53abc10)] __-__ test ci (#6169) (*Joan Fontanals*)

### 🍹 Other Improvements

- [[```9dcd0c18```](https://github.com/jina-ai/jina/commit/9dcd0c1889904307387b58481bec79a808de40a9)] __-__ __docs__: update TOC (*Jina Dev Bot*)
- [[```287fa618```](https://github.com/jina-ai/jina/commit/287fa61873d761187b965eeb44d09ad471a97f7b)] __-__ __version__: the next version will be 3.25.2 (*Jina Dev Bot*)

2 changes: 1 addition & 1 deletion docs/_versions.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"version": "v3.25.1"}, {"version": "v3.25.0"}, {"version": "v3.24.1"}, {"version": "v3.24.0"}, {"version": "v3.23.5"}, {"version": "v3.23.4"}, {"version": "v3.23.3"}, {"version": "v3.23.2"}, {"version": "v3.23.1"}, {"version": "v3.23.0"}, {"version": "v3.22.4"}, {"version": "v3.22.3"}, {"version": "v3.22.2"}, {"version": "v3.22.1"}, {"version": "v3.22.0"}, {"version": "v3.21.1"}, {"version": "v3.21.0"}, {"version": "v3.20.3"}, {"version": "v3.20.2"}, {"version": "v3.20.1"}, {"version": "v3.20.0"}, {"version": "v3.19.1"}, {"version": "v3.19.0"}, {"version": "v3.18.0"}, {"version": "v3.17.0"}, {"version": "v3.16.1"}, {"version": "v3.16.0"}, {"version": "v3.15.2"}, {"version": "v3.15.0"}, {"version": "v3.14.1"}, {"version": "v3.14.0"}, {"version": "v3.13.1"}, {"version": "v3.13.0"}, {"version": "v3.12.0"}, {"version": "v3.11.0"}, {"version": "v3.10.1"}, {"version": "v3.10.0"}, {"version": "v3.9.3"}, {"version": "v3.9.2"}, {"version": "v3.9.1"}, {"version": "v3.9.0"}, {"version": "v3.8.4"}, {"version": "v3.8.0"}, {"version": "v3.7.14"}, {"version": "v3.7.0"}, {"version": "v3.6.16"}, {"version": "v3.6.1"}, {"version": "v3.6.0"}, {"version": "v3.5.0"}, {"version": "v3.4.11"}, {"version": "v3.4.0"}, {"version": "v3.3.25"}, {"version": "v3.3.0"}, {"version": "v3.2.10"}, {"version": "v3.2.0"}]
[{"version": "v3.25.2"}, {"version": "v3.25.1"}, {"version": "v3.25.0"}, {"version": "v3.24.1"}, {"version": "v3.24.0"}, {"version": "v3.23.5"}, {"version": "v3.23.4"}, {"version": "v3.23.3"}, {"version": "v3.23.2"}, {"version": "v3.23.1"}, {"version": "v3.23.0"}, {"version": "v3.22.4"}, {"version": "v3.22.3"}, {"version": "v3.22.2"}, {"version": "v3.22.1"}, {"version": "v3.22.0"}, {"version": "v3.21.1"}, {"version": "v3.21.0"}, {"version": "v3.20.3"}, {"version": "v3.20.2"}, {"version": "v3.20.1"}, {"version": "v3.20.0"}, {"version": "v3.19.1"}, {"version": "v3.19.0"}, {"version": "v3.18.0"}, {"version": "v3.17.0"}, {"version": "v3.16.1"}, {"version": "v3.16.0"}, {"version": "v3.15.2"}, {"version": "v3.15.0"}, {"version": "v3.14.1"}, {"version": "v3.14.0"}, {"version": "v3.13.1"}, {"version": "v3.13.0"}, {"version": "v3.12.0"}, {"version": "v3.11.0"}, {"version": "v3.10.1"}, {"version": "v3.10.0"}, {"version": "v3.9.3"}, {"version": "v3.9.2"}, {"version": "v3.9.1"}, {"version": "v3.9.0"}, {"version": "v3.8.4"}, {"version": "v3.8.0"}, {"version": "v3.7.14"}, {"version": "v3.7.0"}, {"version": "v3.6.16"}, {"version": "v3.6.1"}, {"version": "v3.6.0"}, {"version": "v3.5.0"}, {"version": "v3.4.11"}, {"version": "v3.4.0"}, {"version": "v3.3.25"}, {"version": "v3.3.0"}, {"version": "v3.2.10"}, {"version": "v3.2.0"}]
2 changes: 1 addition & 1 deletion jina/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def _ignore_google_warnings():
# do not change this line manually this is managed by git tag and updated on every release
# NOTE: this represents the NEXT release version

__version__ = '3.25.2'
__version__ = '3.25.3'

# do not change this line manually
# this is managed by proto/build-proto.sh and updated on every execution
Expand Down
23 changes: 22 additions & 1 deletion jina/logging/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class JinaLogger:
:return:: an executor object.
"""

supported = {'FileHandler', 'StreamHandler', 'SysLogHandler', 'RichHandler'}
supported = {'FileHandler', 'StreamHandler', 'SysLogHandler', 'RichHandler', 'TimedRotatingFileHandler', 'RotatingFileHandler'}

def __init__(
self,
Expand Down Expand Up @@ -246,6 +246,27 @@ def add_handlers(self, config_path: Optional[str] = None, **kwargs):
handler = logging.FileHandler(filename, delay=True)
handler.setFormatter(fmt(cfg['format'].format_map(kwargs)))

elif h == 'TimedRotatingFileHandler':
filename = cfg['filename'].format_map(kwargs)
handler = logging.handlers.TimedRotatingFileHandler(
filename=filename,
when=cfg['when'],
interval=cfg['interval'],
backupCount=cfg['backupCount'],
encoding=cfg.get('encoding', 'utf-8')
)
handler.setFormatter(fmt(cfg['format'].format_map(kwargs)))

elif h == 'RotatingFileHandler':
filename = cfg['filename'].format_map(kwargs)
handler = logging.handlers.RotatingFileHandler(
filename=filename,
maxBytes=cfg['maxBytes'],
backupCount=cfg['backupCount'],
encoding=cfg.get('encoding', 'utf-8')
)
handler.setFormatter(fmt(cfg['format'].format_map(kwargs)))

if handler:
self.logger.addHandler(handler)

Expand Down
31 changes: 16 additions & 15 deletions jina/serve/executors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,25 +634,26 @@ def _validate_sagemaker(self):
and self.runtime_args.provider_endpoint
):
endpoint_to_use = ('/' + self.runtime_args.provider_endpoint).lower()
if endpoint_to_use in list(self.requests.keys()):
self.logger.warning(
f'Using "{endpoint_to_use}" as "/invocations" route'
)
self.requests['/invocations'] = self.requests[endpoint_to_use]
for k in remove_keys:
self.requests.pop(k)
return

if len(self.requests) == 1:
route = list(self.requests.keys())[0]
self.logger.warning(f'Using "{route}" as "/invocations" route')
self.requests['/invocations'] = self.requests[route]
elif len(self.requests) == 1:
endpoint_to_use = list(self.requests.keys())[0]
else:
raise ValueError('Cannot identify the endpoint to use for "/invocations"')

if endpoint_to_use in list(self.requests.keys()):
self.logger.warning(f'Using "{endpoint_to_use}" as "/invocations" route')
self.requests['/invocations'] = self.requests[endpoint_to_use]
if (
getattr(self, 'dynamic_batching', {}).get(endpoint_to_use, None)
is not None
):
self.dynamic_batching['/invocations'] = self.dynamic_batching[
endpoint_to_use
]
self.dynamic_batching.pop(endpoint_to_use)
for k in remove_keys:
self.requests.pop(k)
return

raise ValueError('Cannot identify the endpoint to use for "/invocations"')

def _add_dynamic_batching(self, _dynamic_batching: Optional[Dict]):
if _dynamic_batching:
self.dynamic_batching = getattr(self, 'dynamic_batching', {})
Expand Down
9 changes: 7 additions & 2 deletions jina/serve/runtimes/worker/http_csp_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,14 @@ def construct_model_from_line(
)
else:
parsed_fields[field_name] = parsed_list
# Handle direct assignment for basic types
# General parsing attempt for other types
else:
parsed_fields[field_name] = field_info.type_(field_str)
if field_str:
try:
parsed_fields[field_name] = field_info.type_(field_str)
except (ValueError, TypeError):
# Fallback to parse_obj_as when type is more complex, e., AnyUrl or ImageBytes
parsed_fields[field_name] = parse_obj_as(field_info.type_, field_str)

return model(**parsed_fields)

Expand Down
9 changes: 9 additions & 0 deletions jina/serve/runtimes/worker/request_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,16 @@ def _init_batchqueue_dict(self):
# Endpoints allow specific configurations while functions allow configs to be applied to all endpoints of the function
dbatch_endpoints = []
dbatch_functions = []
request_models_map = self._executor._get_endpoint_models_dict()

for key, dbatch_config in self._executor.dynamic_batching.items():
if request_models_map.get(key, {}).get('parameters', {}).get('model', None) is not None:
error_msg = f'Executor Dynamic Batching cannot be used for endpoint {key} because it depends on parameters.'
self.logger.error(
error_msg
)
raise Exception(error_msg)

if key.startswith('/'):
dbatch_endpoints.append((key, dbatch_config))
else:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SampleClipExecutor

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
jtype: SampleClipExecutor
py_modules:
- executor.py
metas:
name: SampleClipExecutor
description:
url:
keywords: []
43 changes: 43 additions & 0 deletions tests/integration/docarray_v2/csp/SampleClipExecutor/executor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from typing import Optional

import numpy as np
from docarray import BaseDoc, DocList
from docarray.typing import NdArray
from docarray.typing.bytes import ImageBytes
from docarray.typing.url import AnyUrl
from jina import Executor, requests
from pydantic import Field


class TextAndImageDoc(BaseDoc):
text: Optional[str] = None
url: Optional[AnyUrl] = None
bytes: Optional[ImageBytes] = None


class EmbeddingResponseModel(TextAndImageDoc):
embeddings: NdArray = Field(description="The embedding of the texts", default=[])

class Config(BaseDoc.Config):
allow_population_by_field_name = True
arbitrary_types_allowed = True
json_encoders = {NdArray: lambda v: v.tolist()}


class SampleClipExecutor(Executor):
@requests(on="/encode")
def foo(
self, docs: DocList[TextAndImageDoc], **kwargs
) -> DocList[EmbeddingResponseModel]:
ret = []
for doc in docs:
ret.append(
EmbeddingResponseModel(
id=doc.id,
text=doc.text,
url=doc.url,
bytes=doc.bytes,
embeddings=np.random.random((1, 64)),
)
)
return DocList[EmbeddingResponseModel](ret)
Empty file.
20 changes: 19 additions & 1 deletion tests/integration/docarray_v2/csp/SampleExecutor/executor.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import numpy as np
from docarray import BaseDoc, DocList
from docarray.typing import NdArray
from pydantic import Field
from pydantic import Field, BaseModel

from jina import Executor, requests

Expand All @@ -19,6 +19,11 @@ class Config(BaseDoc.Config):
json_encoders = {NdArray: lambda v: v.tolist()}


class Parameters(BaseModel):
emb_dim: int



class SampleExecutor(Executor):
@requests(on="/encode")
def foo(self, docs: DocList[TextDoc], **kwargs) -> DocList[EmbeddingResponseModel]:
Expand All @@ -32,3 +37,16 @@ def foo(self, docs: DocList[TextDoc], **kwargs) -> DocList[EmbeddingResponseMode
)
)
return DocList[EmbeddingResponseModel](ret)

@requests(on="/encode_parameter")
def bar(self, docs: DocList[TextDoc], parameters: Parameters, **kwargs) -> DocList[EmbeddingResponseModel]:
ret = []
for doc in docs:
ret.append(
EmbeddingResponseModel(
id=doc.id,
text=doc.text,
embeddings=np.random.random((1, parameters.emb_dim)),
)
)
return DocList[EmbeddingResponseModel](ret)
89 changes: 89 additions & 0 deletions tests/integration/docarray_v2/csp/test_sagemaker_clip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import csv
import io
import os

import requests
from jina.orchestrate.pods import Pod
from jina.parsers import set_pod_parser

sagemaker_port = 8080


def test_provider_sagemaker_pod_rank():
args, _ = set_pod_parser().parse_known_args(
[
"--uses",
os.path.join(os.path.dirname(__file__), "SampleClipExecutor", "config.yml"),
"--provider",
"sagemaker",
"--provider-endpoint",
"encode",
"serve", # This is added by sagemaker
]
)
with Pod(args):
# Test the `GET /ping` endpoint (added by jina for sagemaker)
resp = requests.get(f"http://localhost:{sagemaker_port}/ping")
assert resp.status_code == 200
assert resp.json() == {}

# Test the `POST /invocations` endpoint for inference
# Note: this endpoint is not implemented in the sample executor
resp = requests.post(
f"http://localhost:{sagemaker_port}/invocations",
json={
"data": [
{"url": "http://google.com"},
]
},
)
assert resp.status_code == 200
resp_json = resp.json()
assert len(resp_json["data"]) == 1
assert len(resp_json["data"][0]["embeddings"][0]) == 64
assert resp_json["data"][0]["url"] == "http://google.com"


def test_provider_sagemaker_pod_batch_transform_valid():
args, _ = set_pod_parser().parse_known_args(
[
"--uses",
os.path.join(os.path.dirname(__file__), "SampleClipExecutor", "config.yml"),
"--provider",
"sagemaker",
"serve", # This is added by sagemaker
]
)
with Pod(args):
# Test `POST /invocations` endpoint for batch-transform with valid input
with open(
os.path.join(os.path.dirname(__file__), "valid_clip_input.csv"), "r"
) as f:
csv_data = f.read()

text = []
for line in csv.reader(
io.StringIO(csv_data),
delimiter=",",
quoting=csv.QUOTE_NONE,
escapechar="\\",
):
text.append(line)

resp = requests.post(
f"http://localhost:{sagemaker_port}/invocations",
headers={
"accept": "application/json",
"content-type": "text/csv",
},
data=csv_data,
)
assert resp.status_code == 200
resp_json = resp.json()
assert len(resp_json["data"]) == 3
assert resp_json["data"][0]["text"] == "the cat is in my house"
assert (
resp_json["data"][1]["url"]
== "https://dummyimage3.com/333/000/fff.jpg&text=embed+this"
)
assert "hWjj1RNtNftP" in resp_json["data"][2]["bytes"]
Loading

0 comments on commit df06a19

Please sign in to comment.