Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Handle the streaming of JSON delimited by newlines #4925

Merged
merged 1 commit into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "1.79.0"
".": "1.78.0"
}
30 changes: 0 additions & 30 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,5 @@
# Changelog

## [1.79.0](https://github.com/googleapis/python-aiplatform/compare/v1.78.0...v1.79.0) (2025-01-28)


### Features

* Add machine_spec, data_persistent_disk_spec, network_spec, euc_config, shielded_vm_config to `.google.cloud.aiplatform.v1beta1.NotebookRuntime` ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Add machine_spec, data_persistent_disk_spec, network_spec, euc_config, shielded_vm_config to message `.google.cloud.aiplatform.v1.NotebookRuntime` ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Add per-modality token count break downs for GenAI APIs ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Add per-modality token count break downs for GenAI APIs ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Add speculative decoding spec to DeployedModel proto ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Enable FeatureGroup IAM Methods in v1beta1 API version ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Enable FeatureView Service Account in v1 API version ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Enable UpdateFeatureMonitor in v1beta1 API version ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Expose code execution tool API to v1 ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* GenAI Evaluation: Release GenAI Evaluation SDK Autorater Config/Tuning/Evaluation to vertexai.preview module. ([4531d08](https://github.com/googleapis/python-aiplatform/commit/4531d08c1c9c4a1949f532be9d2beddc53d154b1))
* Reasoning Engine v1 GAPIC release ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Remove autorater config related visibility v1beta1 ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Update supported locations ([50a66b7](https://github.com/googleapis/python-aiplatform/commit/50a66b778fec5d5bfc87536888e0b835bd8d9db2))
* Upload aggregate metrics and generative models used in an evaluation to GCS. ([713ffac](https://github.com/googleapis/python-aiplatform/commit/713ffac6f6f19408bf629dedc06450cee493dae2))


### Documentation

* Deprecate `is_default` in message `.google.cloud.aiplatform.v1.NotebookRuntimeTemplate` ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Deprecate `is_default` in message `.google.cloud.aiplatform.v1beta1.NotebookRuntimeTemplate` ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Deprecate `service_account` in message `.google.cloud.aiplatform.v1.NotebookRuntime` ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Deprecate `service_account` in message `.google.cloud.aiplatform.v1.NotebookRuntimeTemplate` ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Deprecate `service_account` in message `.google.cloud.aiplatform.v1beta1.NotebookRuntime` ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))
* Deprecate `service_account` in message `.google.cloud.aiplatform.v1beta1.NotebookRuntimeTemplate` ([4620e6f](https://github.com/googleapis/python-aiplatform/commit/4620e6f0b0623e18b6e676f4183e972c8ad7e89e))

## [1.78.0](https://github.com/googleapis/python-aiplatform/compare/v1.77.0...v1.78.0) (2025-01-21)


Expand Down
2 changes: 1 addition & 1 deletion google/cloud/aiplatform/gapic_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
2 changes: 1 addition & 1 deletion google/cloud/aiplatform/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
# limitations under the License.
#

__version__ = "1.79.0"
__version__ = "1.78.0"
2 changes: 1 addition & 1 deletion google/cloud/aiplatform_v1/gapic_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
2 changes: 1 addition & 1 deletion google/cloud/aiplatform_v1beta1/gapic_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = "1.79.0" # {x-release-please-version}
__version__ = "1.78.0" # {x-release-please-version}
2 changes: 1 addition & 1 deletion pypi/_vertex_ai_placeholder/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
# limitations under the License.
#

__version__ = "1.79.0"
__version__ = "1.78.0"
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
],
"language": "PYTHON",
"name": "google-cloud-aiplatform",
"version": "1.79.0"
"version": "0.1.0"
},
"snippets": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
],
"language": "PYTHON",
"name": "google-cloud-aiplatform",
"version": "1.79.0"
"version": "0.1.0"
},
"snippets": [
{
Expand Down
30 changes: 14 additions & 16 deletions tests/unit/vertex_langchain/test_reasoning_engines.py
Original file line number Diff line number Diff line change
Expand Up @@ -2234,60 +2234,58 @@ class ToParsedJsonTest(parameterized.TestCase):
obj=httpbody_pb2.HttpBody(
content_type="application/json", data=b'{"a": 1, "b": "hello"}'
),
expected={"a": 1, "b": "hello"},
expected=[{"a": 1, "b": "hello"}],
),
dict(
testcase_name="invalid_json",
obj=httpbody_pb2.HttpBody(
content_type="application/json", data=b'{"a": 1, "b": "hello"'
),
expected=httpbody_pb2.HttpBody(
content_type="application/json", data=b'{"a": 1, "b": "hello"'
),
expected=['{"a": 1, "b": "hello"'], # returns the unparsed string
),
dict(
testcase_name="missing_content_type",
obj=httpbody_pb2.HttpBody(data=b'{"a": 1}'),
expected=httpbody_pb2.HttpBody(data=b'{"a": 1}'),
expected=[httpbody_pb2.HttpBody(data=b'{"a": 1}')],
),
dict(
testcase_name="missing_data",
obj=httpbody_pb2.HttpBody(content_type="application/json"),
expected=None,
expected=[None],
),
dict(
testcase_name="wrong_content_type",
obj=httpbody_pb2.HttpBody(content_type="text/plain", data=b"hello"),
expected=httpbody_pb2.HttpBody(content_type="text/plain", data=b"hello"),
expected=[httpbody_pb2.HttpBody(content_type="text/plain", data=b"hello")],
),
dict(
testcase_name="empty_data",
obj=httpbody_pb2.HttpBody(content_type="application/json", data=b""),
expected=None,
expected=[None],
),
dict(
testcase_name="unicode_data",
obj=httpbody_pb2.HttpBody(
content_type="application/json", data='{"a": "你好"}'.encode("utf-8")
),
expected={"a": "你好"},
expected=[{"a": "你好"}],
),
dict(
testcase_name="nested_json",
obj=httpbody_pb2.HttpBody(
content_type="application/json", data=b'{"a": {"b": 1}}'
),
expected={"a": {"b": 1}},
expected=[{"a": {"b": 1}}],
),
dict(
testcase_name="error_handling",
testcase_name="multiline_json",
obj=httpbody_pb2.HttpBody(
content_type="application/json", data=b'{"a": 1, "b": "hello"'
),
expected=httpbody_pb2.HttpBody(
content_type="application/json", data=b'{"a": 1, "b": "hello"'
content_type="application/json",
data=b'{"a": {"b": 1}}\n{"a": {"b": 2}}',
),
expected=[{"a": {"b": 1}}, {"a": {"b": 2}}],
),
)
def test_to_parsed_json(self, obj, expected):
self.assertEqual(_utils.to_parsed_json(obj), expected)
for got, want in zip(_utils.yield_parsed_json(obj), expected):
self.assertEqual(got, want)
6 changes: 3 additions & 3 deletions vertexai/reasoning_engines/_reasoning_engines.py
Original file line number Diff line number Diff line change
Expand Up @@ -840,9 +840,9 @@ def _method(self, **kwargs) -> Iterable[Any]:
),
)
for chunk in response:
parsed_json = _utils.to_parsed_json(chunk)
if parsed_json is not None:
yield parsed_json
for parsed_json in _utils.yield_parsed_json(chunk):
if parsed_json is not None:
yield parsed_json

_method.__name__ = method_name
_method.__doc__ = doc
Expand Down
34 changes: 20 additions & 14 deletions vertexai/reasoning_engines/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import json
import types
import typing
from typing import Any, Callable, Dict, Mapping, Optional, Sequence, Union
from typing import Any, Callable, Dict, Iterable, Mapping, Optional, Sequence, Union

import proto

Expand Down Expand Up @@ -90,36 +90,42 @@ def to_dict(message: proto.Message) -> JsonDict:
return result


def to_parsed_json(body: httpbody_pb2.HttpBody) -> Any:
def yield_parsed_json(body: httpbody_pb2.HttpBody) -> Iterable[Any]:
"""Converts the contents of the httpbody message to JSON format.

Args:
body (httpbody_pb2.HttpBody):
Required. The httpbody body to be converted to a JSON.

Returns:
Yields:
Any: A JSON object or the original body if it is not JSON or None.
"""
content_type = getattr(body, "content_type", None)
data = getattr(body, "data", None)

if content_type is None or data is None or "application/json" not in content_type:
return body
yield body
return

try:
utf8_data = data.decode("utf-8")
except Exception as e:
_LOGGER.warning(f"Failed to decode data: {data}. Exception: {e}")
return body
yield body
return

if not utf8_data:
return None
yield None
return

try:
return json.loads(utf8_data)
except Exception as e:
_LOGGER.warning(f"Failed to parse JSON: {utf8_data}. Exception: {e}")
return body # Return the raw body on error
# Handle the case of multiple dictionaries delimited by newlines.
for line in utf8_data.split("\n"):
if line:
try:
line = json.loads(line)
except Exception as e:
_LOGGER.warning(f"failed to parse json: {line}. Exception: {e}")
yield line


def generate_schema(
Expand Down Expand Up @@ -195,9 +201,9 @@ def generate_schema(
# * https://github.com/pydantic/pydantic/issues/1270
# * https://stackoverflow.com/a/58841311
# * https://github.com/pydantic/pydantic/discussions/4872
if typing.get_origin(annotation) is typing.Union and type(
None
) in typing.get_args(annotation):
if typing.get_origin(annotation) is Union and type(None) in typing.get_args(
annotation
):
# for "typing.Optional" arguments, function_arg might be a
# dictionary like
#
Expand Down