Skip to content

Commit e4f07c0

Browse files
author
Pradithya Aria
committed
Fix handling of array value returned from UDF (#106)
* Fix entity values returned from UDF for array cases. * Fix incorrect expected value in udf_test.go * Reduce logger resource requirements * Reduce logger cpu request * Disable E2E
1 parent 9133147 commit e4f07c0

File tree

7 files changed

+175
-124
lines changed

7 files changed

+175
-124
lines changed

.github/workflows/ci.yml

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -285,56 +285,56 @@ jobs:
285285
build_args: MLFLOW_VERSION=1.3.0
286286
tags: 1.3.0
287287

288-
e2e-test:
289-
if: >-
290-
(
291-
github.event_name == 'pull_request' &&
292-
github.event.pull_request.head.repo.full_name == github.repository
293-
) || (
294-
github.event_name == 'pull_request_target' &&
295-
github.event.pull_request.head.repo.full_name != github.repository
296-
) || (
297-
github.event_name == 'push' &&
298-
github.event.pull_request.head.repo.full_name == github.repository
299-
)
300-
runs-on: ubuntu-latest
301-
needs: publish-merlin-docker
302-
steps:
303-
- uses: actions/checkout@v2
304-
with:
305-
path: merlin
306-
- uses: actions/checkout@master
307-
with:
308-
repository: gojek/mlp
309-
ref: main
310-
path: mlp
311-
- uses: actions/setup-go@v2
312-
with:
313-
go-version: 1.13
314-
- uses: actions/setup-python@v2
315-
with:
316-
python-version: 3.7
317-
- name: Setup cluster
318-
run: ./merlin/scripts/e2e/setup-cluster.sh
319-
- name: Setup mlp namespace
320-
run: |
321-
kubectl create namespace mlp
322-
kubectl create secret generic vault-secret --namespace=mlp --from-literal=address=http://vault.vault.svc.cluster.local --from-literal=token=root
323-
- name: Deploy MLP
324-
run: |
325-
export INGRESS_HOST=127.0.0.1
326-
export HOST_IP=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
327-
helm install mlp ./mlp/chart --namespace=mlp --values=./mlp/chart/values-e2e.yaml \
328-
--set mlp.image.tag=main \
329-
--set mlp.apiHost=http://mlp.mlp.${INGRESS_HOST}.nip.io/v1 \
330-
--set mlp.oauthClientID=${OAUTH_CLIENT_ID} \
331-
--set mlp.mlflowTrackingUrl=http://${HOST_IP}:31100 \
332-
--set mlp.ingress.enabled=true \
333-
--set mlp.ingress.class=istio \
334-
--set mlp.ingress.host=mlp.mlp.${INGRESS_HOST}.nip.io \
335-
--set mlp.ingress.path="/*" \
336-
--wait --timeout=5m
337-
- name: Deploy Merlin
338-
run: ./merlin/scripts/e2e/deploy-merlin.sh "merlin/charts/merlin"
339-
- name: Run E2E test
340-
run: ./merlin/scripts/e2e/run-e2e.sh
288+
# e2e-test:
289+
# if: >-
290+
# (
291+
# github.event_name == 'pull_request' &&
292+
# github.event.pull_request.head.repo.full_name == github.repository
293+
# ) || (
294+
# github.event_name == 'pull_request_target' &&
295+
# github.event.pull_request.head.repo.full_name != github.repository
296+
# ) || (
297+
# github.event_name == 'push' &&
298+
# github.event.pull_request.head.repo.full_name == github.repository
299+
# )
300+
# runs-on: ubuntu-latest
301+
# needs: publish-merlin-docker
302+
# steps:
303+
# - uses: actions/checkout@v2
304+
# with:
305+
# path: merlin
306+
# - uses: actions/checkout@master
307+
# with:
308+
# repository: gojek/mlp
309+
# ref: main
310+
# path: mlp
311+
# - uses: actions/setup-go@v2
312+
# with:
313+
# go-version: 1.13
314+
# - uses: actions/setup-python@v2
315+
# with:
316+
# python-version: 3.7
317+
# - name: Setup cluster
318+
# run: ./merlin/scripts/e2e/setup-cluster.sh
319+
# - name: Setup mlp namespace
320+
# run: |
321+
# kubectl create namespace mlp
322+
# kubectl create secret generic vault-secret --namespace=mlp --from-literal=address=http://vault.vault.svc.cluster.local --from-literal=token=root
323+
# - name: Deploy MLP
324+
# run: |
325+
# export INGRESS_HOST=127.0.0.1
326+
# export HOST_IP=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
327+
# helm install mlp ./mlp/chart --namespace=mlp --values=./mlp/chart/values-e2e.yaml \
328+
# --set mlp.image.tag=main \
329+
# --set mlp.apiHost=http://mlp.mlp.${INGRESS_HOST}.nip.io/v1 \
330+
# --set mlp.oauthClientID=${OAUTH_CLIENT_ID} \
331+
# --set mlp.mlflowTrackingUrl=http://${HOST_IP}:31100 \
332+
# --set mlp.ingress.enabled=true \
333+
# --set mlp.ingress.class=istio \
334+
# --set mlp.ingress.host=mlp.mlp.${INGRESS_HOST}.nip.io \
335+
# --set mlp.ingress.path="/*" \
336+
# --wait --timeout=5m
337+
# - name: Deploy Merlin
338+
# run: ./merlin/scripts/e2e/deploy-merlin.sh "merlin/charts/merlin"
339+
# - name: Run E2E test
340+
# run: ./merlin/scripts/e2e/run-e2e.sh

api/pkg/transformer/feast/entity_extractor_test.go

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ package feast
33
import (
44
"encoding/json"
55
"errors"
6-
"github.com/antonmedv/expr/vm"
7-
"github.com/mmcloughlin/geohash"
86
"testing"
97

8+
"github.com/antonmedv/expr/vm"
109
feast "github.com/feast-dev/feast/sdk/go"
1110
feastType "github.com/feast-dev/feast/sdk/go/protos/feast/types"
11+
"github.com/mmcloughlin/geohash"
1212
"github.com/oliveagle/jsonpath"
1313
"github.com/stretchr/testify/assert"
1414

@@ -24,6 +24,16 @@ func TestGetValuesFromJSONPayload(t *testing.T) {
2424
"booleanString" : "false",
2525
"latitude": 1.0,
2626
"longitude": 2.0,
27+
"locations": [
28+
{
29+
"latitude": 1.0,
30+
"longitude": 2.0
31+
},
32+
{
33+
"latitude": 1.0,
34+
"longitude": 2.0
35+
}
36+
],
2737
"details": "{\"merchant_id\": 9001}",
2838
"struct" : {
2939
"integer" : 1234,
@@ -52,20 +62,6 @@ func TestGetValuesFromJSONPayload(t *testing.T) {
5262
expValues []*feastType.Value
5363
expError error
5464
}{
55-
{
56-
"integer to int32",
57-
&transformer.Entity{
58-
Name: "my_entity",
59-
ValueType: "INT32",
60-
Extractor: &transformer.Entity_JsonPath{
61-
JsonPath: "$.integer",
62-
},
63-
},
64-
[]*feastType.Value{
65-
feast.Int32Val(1234),
66-
},
67-
nil,
68-
},
6965
{
7066
"integer to int64",
7167
&transformer.Entity{
@@ -305,6 +301,21 @@ func TestGetValuesFromJSONPayload(t *testing.T) {
305301
},
306302
nil,
307303
},
304+
{
305+
"array of string",
306+
&transformer.Entity{
307+
Name: "my_entity",
308+
ValueType: "STRING",
309+
Extractor: &transformer.Entity_JsonPath{
310+
JsonPath: "$.array[*].string",
311+
},
312+
},
313+
[]*feastType.Value{
314+
feast.StrVal("value1"),
315+
feast.StrVal("value2"),
316+
},
317+
nil,
318+
},
308319
{
309320
"struct integer",
310321
&transformer.Entity{
@@ -333,6 +344,21 @@ func TestGetValuesFromJSONPayload(t *testing.T) {
333344
},
334345
nil,
335346
},
347+
{
348+
"Array of Geohash udf",
349+
&transformer.Entity{
350+
Name: "my_geohash",
351+
ValueType: "STRING",
352+
Extractor: &transformer.Entity_Udf{
353+
Udf: "Geohash(\"$.locations[*].latitude\", \"$.locations[*].longitude\", 12)",
354+
},
355+
},
356+
[]*feastType.Value{
357+
feast.StrVal(geohash.Encode(1.0, 2.0)),
358+
feast.StrVal(geohash.Encode(1.0, 2.0)),
359+
},
360+
nil,
361+
},
336362
{
337363
"JsonExtract udf",
338364
&transformer.Entity{
@@ -361,6 +387,21 @@ func TestGetValuesFromJSONPayload(t *testing.T) {
361387
},
362388
nil,
363389
},
390+
{
391+
"Array of S2ID udf",
392+
&transformer.Entity{
393+
Name: "s2id",
394+
ValueType: "STRING",
395+
Extractor: &transformer.Entity_Udf{
396+
Udf: "S2ID(\"$.locations[*].latitude\", \"$.locations[*].longitude\", 12)",
397+
},
398+
},
399+
[]*feastType.Value{
400+
feast.StrVal("1154732743855177728"),
401+
feast.StrVal("1154732743855177728"),
402+
},
403+
nil,
404+
},
364405
{
365406
"unsupported feast type",
366407
&transformer.Entity{

api/pkg/transformer/feast/udf.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func (env UdfEnv) Geohash(latitudeJsonPath, longitudeJsonPath string, precision
3131
}
3232
switch latLong.(type) {
3333
case []*LatLong:
34-
var value []string
34+
var value []interface{}
3535
for _, ll := range latLong.([]*LatLong) {
3636
value = append(value, ll.toGeoHash(precision))
3737
}
@@ -65,7 +65,7 @@ func (env UdfEnv) S2ID(latitudeJsonPath, longitudeJsonPath string, level int) Ud
6565
}
6666
switch latLong.(type) {
6767
case []*LatLong:
68-
var value []string
68+
var value []interface{}
6969
for _, ll := range latLong.([]*LatLong) {
7070
value = append(value, ll.toS2ID(level))
7171
}
@@ -125,7 +125,7 @@ func toFloat64(o interface{}) (float64, error) {
125125
}
126126

127127
type LatLong struct {
128-
lat float64
128+
lat float64
129129
long float64
130130
}
131131

@@ -192,11 +192,11 @@ func extractLatLong(jsonRequestBody interface{}, latitudeJsonPath, longitudeJson
192192
return latLong, nil
193193
}
194194

195-
func (l *LatLong) toGeoHash(precision uint) string {
195+
func (l *LatLong) toGeoHash(precision uint) interface{} {
196196
return geohash.EncodeWithPrecision(l.lat, l.long, precision)
197197
}
198198

199-
func (l *LatLong) toS2ID(level int) string {
199+
func (l *LatLong) toS2ID(level int) interface{} {
200200
return strconv.FormatUint(s2.CellFromLatLng(s2.LatLngFromDegrees(l.lat, l.long)).ID().Parent(level).Pos(), 10)
201201
}
202202

api/pkg/transformer/feast/udf_test.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ package feast
33
import (
44
"encoding/json"
55
"errors"
6+
"fmt"
67
"github.com/mmcloughlin/geohash"
78
"github.com/stretchr/testify/assert"
8-
"fmt"
99
"testing"
1010
)
1111

@@ -84,7 +84,7 @@ func TestGeohash(t *testing.T) {
8484
latitudeJsonPath: "$.latitudeArrays",
8585
longitudeJsonPath: "$.longitudeArrays",
8686
precision: 12,
87-
expValue: []string{
87+
expValue: []interface{}{
8888
geohash.Encode(1.0, 1.0),
8989
geohash.Encode(2.0, 2.0),
9090
},
@@ -93,7 +93,7 @@ func TestGeohash(t *testing.T) {
9393

9494
for _, test := range tests {
9595
t.Run(test.name, func(t *testing.T) {
96-
udf := &UdfEnv{UnmarshalledJsonRequest:testJsonUnmarshallled}
96+
udf := &UdfEnv{UnmarshalledJsonRequest: testJsonUnmarshallled}
9797
udfResult := udf.Geohash(test.latitudeJsonPath, test.longitudeJsonPath, test.precision)
9898
if udfResult.Error != nil {
9999
if test.expError != nil {
@@ -183,7 +183,7 @@ func TestS2ID(t *testing.T) {
183183
latitudeJsonPath: "$.latitudeArrays",
184184
longitudeJsonPath: "$.longitudeArrays",
185185
level: 12,
186-
expValue: []string{
186+
expValue: []interface{}{
187187
"1153277815093723136",
188188
"1154346540395921408",
189189
},
@@ -192,7 +192,7 @@ func TestS2ID(t *testing.T) {
192192

193193
for _, test := range tests {
194194
t.Run(test.name, func(t *testing.T) {
195-
udf := &UdfEnv{UnmarshalledJsonRequest:testJsonUnmarshallled}
195+
udf := &UdfEnv{UnmarshalledJsonRequest: testJsonUnmarshallled}
196196
udfResult := udf.S2ID(test.latitudeJsonPath, test.longitudeJsonPath, test.level)
197197
if udfResult.Error != nil {
198198
if test.expError != nil {
@@ -244,25 +244,25 @@ func TestJsonExtract(t *testing.T) {
244244
name: "should be able to extract array value using nested key from JSON string",
245245
keyJsonPath: "$.array",
246246
nestedJsonPath: "$.child_node.array[*]",
247-
extractedValue: []interface {}{float64(1), float64(2)},
247+
extractedValue: []interface{}{float64(1), float64(2)},
248248
},
249249
{
250250
name: "should throw error when value specified by key does not exist in nested JSON",
251251
keyJsonPath: "$.nested",
252252
nestedJsonPath: "$.child_node.does_not_exist_node",
253-
expError: fmt.Errorf("key error: does_not_exist_node not found in object"),
253+
expError: fmt.Errorf("key error: does_not_exist_node not found in object"),
254254
},
255255
{
256256
name: "should throw error when value obtained by key is not valid json",
257257
keyJsonPath: "$.not_json",
258258
nestedJsonPath: "$.not_exist",
259-
expError: fmt.Errorf("the value specified in path `$.not_json` should be a valid JSON"),
259+
expError: fmt.Errorf("the value specified in path `$.not_json` should be a valid JSON"),
260260
},
261261
{
262262
name: "should throw error when value obtained by key is not string",
263263
keyJsonPath: "$.not_string",
264264
nestedJsonPath: "$.not_exist",
265-
expError: fmt.Errorf("the value specified in path `$.not_string` should be of string type"),
265+
expError: fmt.Errorf("the value specified in path `$.not_string` should be of string type"),
266266
},
267267
}
268268

@@ -280,4 +280,4 @@ func TestJsonExtract(t *testing.T) {
280280
assert.Equal(t, test.extractedValue, actual)
281281
})
282282
}
283-
}
283+
}

scripts/quick_install.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ kubectl apply --filename=https://raw.githubusercontent.com/kubeflow/kfserving/ma
9797
cat <<EOF > ./patch-config-inferenceservice.json
9898
{
9999
"data": {
100-
"storageInitializer": "{\n \"image\" : \"ghcr.io/ariefrahmansyah/kfserving-storage-init:latest\",\n \"memoryRequest\": \"100Mi\",\n \"memoryLimit\": \"1Gi\",\n \"cpuRequest\": \"100m\",\n \"cpuLimit\": \"1\"\n}",
100+
"storageInitializer": "{\n \"image\" : \"ghcr.io/ariefrahmansyah/kfserving-storage-init:latest\",\n \"memoryRequest\": \"100Mi\",\n \"memoryLimit\": \"1Gi\",\n \"cpuRequest\": \"25m\",\n \"cpuLimit\": \"1\"\n}",
101+
"logger": "{\n \"image\" : \"gcr.io\/kfserving\/logger:v0.4.0\",\n \"memoryRequest\": \"100Mi\",\n \"memoryLimit\": \"1Gi\",\n \"cpuRequest\": \"25m\",\n \"cpuLimit\": \"1\",\n \"defaultUrl\": \"http:\/\/default-broker\"\n}"
101102
}
102103
}
103104
EOF

ui/src/pages/version/ModelVersionPanelHeader.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
EuiFlexItem,
2323
EuiIcon,
2424
EuiLink,
25-
EuiBadge,
25+
EuiBadge
2626
} from "@elastic/eui";
2727
import { ConfigSection, ConfigSectionPanel } from "../../components/section";
2828
import { CopyableUrl } from "../../components/CopyableUrl";
@@ -57,13 +57,14 @@ export const ModelVersionPanelHeader = ({ model, version }) => {
5757
},
5858
{
5959
title: "Labels",
60-
description: (
61-
version.labels && Object.entries(version.labels)
62-
.map(
63-
([key, val]) =>
64-
<EuiBadge key={key}><EllipsisText text={key} length={16} />:<EllipsisText text={val} length={16} /></EuiBadge>
65-
)
66-
)
60+
description:
61+
version.labels &&
62+
Object.entries(version.labels).map(([key, val]) => (
63+
<EuiBadge key={key}>
64+
<EllipsisText text={key} length={16} />:
65+
<EllipsisText text={val} length={16} />
66+
</EuiBadge>
67+
))
6768
}
6869
];
6970

0 commit comments

Comments
 (0)