@michelangelo-ai/rpc 0.9.0 → 0.10.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/services.test.ts +188 -5
- package/handlers.ts +20 -0
- package/pack-any-fields.ts +92 -0
- package/package.json +2 -2
- package/request.ts +21 -0
- package/services.ts +24 -2
- package/types.ts +2 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { create } from '@bufbuild/protobuf';
|
|
2
|
+
import { anyPack, StringValueSchema } from '@bufbuild/protobuf/wkt';
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
4
|
|
|
3
5
|
import { request } from '../request';
|
|
4
6
|
|
|
@@ -40,8 +42,189 @@ it('decodes a ListPipelineRun response containing a TypedStruct Any field', asyn
|
|
|
40
42
|
result as unknown as { pipelineRunList: { items: { status: { details: unknown[] } }[] } }
|
|
41
43
|
).pipelineRunList.items[0].status.details;
|
|
42
44
|
|
|
43
|
-
// The
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
expect(
|
|
45
|
+
// The registry decodes the Any to a binary TypedStruct, and toPlainObject unpacks it to
|
|
46
|
+
// { typeUrl, value } where typeUrl names the inner config type and value is its plain
|
|
47
|
+
// JSON. Without TypedStructSchema in the registry, fromJson throws before reaching here.
|
|
48
|
+
expect(details[0]).toEqual({
|
|
49
|
+
typeUrl: 'type.googleapis.com/michelangelo.UniFlowConf',
|
|
50
|
+
value: {},
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Verifies the Any lands on the wire in the shape Envoy's grpc_json_transcoder expects.
|
|
55
|
+
describe('outgoing request — Any-packing through the real service client', () => {
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
vi.mocked(global.fetch).mockClear();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
function expectCriteria(
|
|
61
|
+
...expected: Array<{ fieldName: string; operator: string; matchValue: unknown }>
|
|
62
|
+
) {
|
|
63
|
+
const calls = vi.mocked(global.fetch).mock.calls;
|
|
64
|
+
const [, init] = calls.at(-1) as [string, RequestInit];
|
|
65
|
+
// cast: this test only cares about the shape it itself constructed
|
|
66
|
+
const body = JSON.parse(init.body as string) as {
|
|
67
|
+
listOptionsExt: { operation: { criterion: Record<string, unknown>[] } };
|
|
68
|
+
};
|
|
69
|
+
const criteria = body.listOptionsExt.operation.criterion;
|
|
70
|
+
expect(criteria).toHaveLength(expected.length);
|
|
71
|
+
for (const [i, criterion] of expected.entries()) {
|
|
72
|
+
expect(criteria[i]).toMatchObject(criterion);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
it('packs a string matchValue into a StringValue Any', async () => {
|
|
77
|
+
await request('ListPipelineRun', {
|
|
78
|
+
listOptionsExt: {
|
|
79
|
+
operation: {
|
|
80
|
+
criterion: [
|
|
81
|
+
{ fieldName: 'pipeline_run.pipeline_name', operator: 1, matchValue: 'my-pipeline' },
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
} as never);
|
|
86
|
+
|
|
87
|
+
expectCriteria({
|
|
88
|
+
fieldName: 'pipeline_run.pipeline_name',
|
|
89
|
+
operator: 'CRITERION_OPERATOR_EQUAL',
|
|
90
|
+
matchValue: {
|
|
91
|
+
'@type': 'type.googleapis.com/google.protobuf.StringValue',
|
|
92
|
+
value: 'my-pipeline',
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('packs a boolean matchValue into a BoolValue Any', async () => {
|
|
98
|
+
await request('ListPipelineRun', {
|
|
99
|
+
listOptionsExt: {
|
|
100
|
+
operation: { criterion: [{ fieldName: 'x', operator: 1, matchValue: true }] },
|
|
101
|
+
},
|
|
102
|
+
} as never);
|
|
103
|
+
|
|
104
|
+
expectCriteria({
|
|
105
|
+
fieldName: 'x',
|
|
106
|
+
operator: 'CRITERION_OPERATOR_EQUAL',
|
|
107
|
+
matchValue: { '@type': 'type.googleapis.com/google.protobuf.BoolValue', value: true },
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('packs an integer matchValue into an Int64Value Any', async () => {
|
|
112
|
+
await request('ListPipelineRun', {
|
|
113
|
+
listOptionsExt: {
|
|
114
|
+
operation: { criterion: [{ fieldName: 'x', operator: 1, matchValue: 42 }] },
|
|
115
|
+
},
|
|
116
|
+
} as never);
|
|
117
|
+
|
|
118
|
+
expectCriteria({
|
|
119
|
+
fieldName: 'x',
|
|
120
|
+
operator: 'CRITERION_OPERATOR_EQUAL',
|
|
121
|
+
matchValue: { '@type': 'type.googleapis.com/google.protobuf.Int64Value', value: '42' },
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('packs a float matchValue into a DoubleValue Any', async () => {
|
|
126
|
+
await request('ListPipelineRun', {
|
|
127
|
+
listOptionsExt: {
|
|
128
|
+
operation: { criterion: [{ fieldName: 'x', operator: 1, matchValue: 1.5 }] },
|
|
129
|
+
},
|
|
130
|
+
} as never);
|
|
131
|
+
|
|
132
|
+
expectCriteria({
|
|
133
|
+
fieldName: 'x',
|
|
134
|
+
operator: 'CRITERION_OPERATOR_EQUAL',
|
|
135
|
+
matchValue: { '@type': 'type.googleapis.com/google.protobuf.DoubleValue', value: 1.5 },
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('sends an already-packed Any (real typeUrl/value) through unchanged', async () => {
|
|
140
|
+
const realAny = anyPack(
|
|
141
|
+
StringValueSchema,
|
|
142
|
+
create(StringValueSchema, { value: 'already-packed' })
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
await request('ListPipelineRun', {
|
|
146
|
+
listOptionsExt: {
|
|
147
|
+
operation: { criterion: [{ fieldName: 'x', operator: 1, matchValue: realAny }] },
|
|
148
|
+
},
|
|
149
|
+
} as never);
|
|
150
|
+
|
|
151
|
+
expectCriteria({
|
|
152
|
+
fieldName: 'x',
|
|
153
|
+
operator: 'CRITERION_OPERATOR_EQUAL',
|
|
154
|
+
matchValue: {
|
|
155
|
+
'@type': 'type.googleapis.com/google.protobuf.StringValue',
|
|
156
|
+
value: 'already-packed',
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('packs Any values across every entry of a repeated field, and leaves fieldName untouched', async () => {
|
|
162
|
+
await request('ListPipelineRun', {
|
|
163
|
+
listOptionsExt: {
|
|
164
|
+
operation: {
|
|
165
|
+
criterion: [
|
|
166
|
+
{ fieldName: 'a', operator: 1, matchValue: 'one' },
|
|
167
|
+
{ fieldName: 'b', operator: 1, matchValue: 'two' },
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
} as never);
|
|
172
|
+
|
|
173
|
+
expectCriteria(
|
|
174
|
+
{
|
|
175
|
+
fieldName: 'a',
|
|
176
|
+
operator: 'CRITERION_OPERATOR_EQUAL',
|
|
177
|
+
matchValue: { '@type': 'type.googleapis.com/google.protobuf.StringValue', value: 'one' },
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
fieldName: 'b',
|
|
181
|
+
operator: 'CRITERION_OPERATOR_EQUAL',
|
|
182
|
+
matchValue: { '@type': 'type.googleapis.com/google.protobuf.StringValue', value: 'two' },
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Rejects rather than letting create() silently turn this into an empty, corrupted Any —
|
|
188
|
+
// callers already normalize thrown RPC errors, so this surfaces as a normal query error.
|
|
189
|
+
it('rejects when an Any field is given a value with no wrapper mapping', async () => {
|
|
190
|
+
await expect(
|
|
191
|
+
request('ListPipelineRun', {
|
|
192
|
+
listOptionsExt: {
|
|
193
|
+
operation: {
|
|
194
|
+
criterion: [{ fieldName: 'x', operator: 1, matchValue: { nested: 'object' } }],
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
} as never)
|
|
198
|
+
).rejects.toThrow(/cannot auto-pack object/);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// A map<string, Any> field on an unrelated service/message, proving the packing is
|
|
202
|
+
// schema-driven rather than special-cased for Criterion.
|
|
203
|
+
it('packs a map<string, Any> field on CreateDeployment, an unrelated service method', async () => {
|
|
204
|
+
(global.fetch as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
205
|
+
status: 200,
|
|
206
|
+
headers: new Headers({ 'content-type': 'application/json' }),
|
|
207
|
+
json: () => Promise.resolve({}),
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
await request('CreateDeployment', {
|
|
211
|
+
metadata: { name: 'my-deployment' },
|
|
212
|
+
status: { providerStatus: { foo: 'bar-value', replicas: 3 } },
|
|
213
|
+
} as never);
|
|
214
|
+
|
|
215
|
+
const calls = (global.fetch as ReturnType<typeof vi.fn>).mock.calls;
|
|
216
|
+
const [, init] = calls.at(-1) as [string, RequestInit];
|
|
217
|
+
const body = JSON.parse(init.body as string) as {
|
|
218
|
+
deployment: { status: { providerStatus: Record<string, unknown> } };
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
expect(body.deployment.status.providerStatus.foo).toEqual({
|
|
222
|
+
'@type': 'type.googleapis.com/google.protobuf.StringValue',
|
|
223
|
+
value: 'bar-value',
|
|
224
|
+
});
|
|
225
|
+
expect(body.deployment.status.providerStatus.replicas).toEqual({
|
|
226
|
+
'@type': 'type.googleapis.com/google.protobuf.Int64Value',
|
|
227
|
+
value: '3',
|
|
228
|
+
});
|
|
229
|
+
});
|
|
47
230
|
});
|
package/handlers.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { create } from '@bufbuild/protobuf';
|
|
|
3
3
|
import { UserInfoSchema } from './gen/michelangelo/api/v2/user_pb';
|
|
4
4
|
import { getServices } from './services';
|
|
5
5
|
|
|
6
|
+
import type { Deployment } from './gen/michelangelo/api/v2/deployment_pb';
|
|
7
|
+
import type { InferenceServer } from './gen/michelangelo/api/v2/inference_server_pb';
|
|
6
8
|
import type { PipelineRun } from './gen/michelangelo/api/v2/pipeline_run_pb';
|
|
7
9
|
import type { TriggerRun } from './gen/michelangelo/api/v2/trigger_run_pb';
|
|
8
10
|
import type { ExtractUnaryRpc } from './types';
|
|
@@ -36,8 +38,17 @@ async function createHandlers() {
|
|
|
36
38
|
return {
|
|
37
39
|
ListDeployment: unary(services.DeploymentService.listDeployment),
|
|
38
40
|
GetDeployment: unary(services.DeploymentService.getDeployment),
|
|
41
|
+
CreateDeployment: (record: Deployment, headers?: Record<string, string>) => {
|
|
42
|
+
const actorName = headers?.['x-user-name'];
|
|
43
|
+
if (actorName && record.spec) {
|
|
44
|
+
record.spec.owner = create(UserInfoSchema, { name: actorName });
|
|
45
|
+
}
|
|
46
|
+
return services.DeploymentService.createDeployment({ deployment: record }, headers);
|
|
47
|
+
},
|
|
39
48
|
ListInferenceServer: unary(services.InferenceServerService.listInferenceServer),
|
|
40
49
|
GetInferenceServer: unary(services.InferenceServerService.getInferenceServer),
|
|
50
|
+
CreateInferenceServer: (record: InferenceServer, headers?: Record<string, string>) =>
|
|
51
|
+
services.InferenceServerService.createInferenceServer({ inferenceServer: record }, headers),
|
|
41
52
|
ListProject: unary(services.ProjectService.listProject),
|
|
42
53
|
GetProject: unary(services.ProjectService.getProject),
|
|
43
54
|
GetPipeline: unary(services.PipelineService.getPipeline),
|
|
@@ -47,6 +58,13 @@ async function createHandlers() {
|
|
|
47
58
|
GetPipelineRun: unary(services.PipelineRunService.getPipelineRun),
|
|
48
59
|
ListTriggerRun: unary(services.TriggerRunService.listTriggerRun),
|
|
49
60
|
GetTriggerRun: unary(services.TriggerRunService.getTriggerRun),
|
|
61
|
+
CreateTriggerRun: (record: TriggerRun, headers?: Record<string, string>) => {
|
|
62
|
+
const actorName = headers?.['x-user-name'];
|
|
63
|
+
if (actorName && record.spec) {
|
|
64
|
+
record.spec.actor = create(UserInfoSchema, { name: actorName });
|
|
65
|
+
}
|
|
66
|
+
return services.TriggerRunService.createTriggerRun({ triggerRun: record }, headers);
|
|
67
|
+
},
|
|
50
68
|
UpdateTriggerRun: (record: TriggerRun, headers?: Record<string, string>) =>
|
|
51
69
|
services.TriggerRunService.updateTriggerRun({ triggerRun: record }, headers),
|
|
52
70
|
CreatePipelineRun: (record: PipelineRun, headers?: Record<string, string>) => {
|
|
@@ -59,6 +77,8 @@ async function createHandlers() {
|
|
|
59
77
|
UpdatePipelineRun: (record: PipelineRun, headers?: Record<string, string>) =>
|
|
60
78
|
services.PipelineRunService.updatePipelineRun({ pipelineRun: record }, headers),
|
|
61
79
|
ListModel: unary(services.ModelService.listModel),
|
|
80
|
+
GetModel: unary(services.ModelService.getModel),
|
|
81
|
+
ListModelFamily: unary(services.ModelFamilyService.listModelFamily),
|
|
62
82
|
} as const;
|
|
63
83
|
}
|
|
64
84
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { create } from '@bufbuild/protobuf';
|
|
2
|
+
import {
|
|
3
|
+
anyPack,
|
|
4
|
+
BoolValueSchema,
|
|
5
|
+
DoubleValueSchema,
|
|
6
|
+
Int64ValueSchema,
|
|
7
|
+
StringValueSchema,
|
|
8
|
+
} from '@bufbuild/protobuf/wkt';
|
|
9
|
+
|
|
10
|
+
import type { DescField, DescMessage } from '@bufbuild/protobuf';
|
|
11
|
+
|
|
12
|
+
const ANY_TYPE_NAME = 'google.protobuf.Any';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Walks a request object against its proto descriptor, packing JS primitives into well-known
|
|
16
|
+
* wrapper types wherever the schema has a `google.protobuf.Any` field.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* packAnyFields(CriterionSchema, { fieldName: "x", matchValue: "my-pipeline" })
|
|
20
|
+
* // matchValue -> anyPack(StringValueSchema, create(StringValueSchema, { value: "my-pipeline" }))
|
|
21
|
+
*/
|
|
22
|
+
export function packAnyFields(desc: DescMessage, value: unknown): unknown {
|
|
23
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) return value;
|
|
24
|
+
|
|
25
|
+
const result: Record<string, unknown> = {};
|
|
26
|
+
for (const [key, val] of Object.entries(value)) {
|
|
27
|
+
const field: DescField | undefined = desc.field[key];
|
|
28
|
+
result[key] = field ? packField(field, val) : val;
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function packField(field: DescField, value: unknown): unknown {
|
|
34
|
+
if (value === null || value === undefined) return value;
|
|
35
|
+
|
|
36
|
+
switch (field.fieldKind) {
|
|
37
|
+
case 'message':
|
|
38
|
+
return packMessageValue(field.message, value);
|
|
39
|
+
case 'list':
|
|
40
|
+
if (field.listKind === 'message') {
|
|
41
|
+
// cast: repeated fields are always arrays at runtime
|
|
42
|
+
return (value as unknown[]).map((item) => packMessageValue(field.message, item));
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
case 'map': {
|
|
46
|
+
const mapValueMessage = field.message;
|
|
47
|
+
if (!mapValueMessage) return value;
|
|
48
|
+
// cast: map fields are always plain objects at runtime, keyed by the (stringified)
|
|
49
|
+
// map key regardless of its declared scalar type
|
|
50
|
+
const mapValue = value as Record<string, unknown>;
|
|
51
|
+
return Object.fromEntries(
|
|
52
|
+
Object.entries(mapValue).map(([key, item]) => [
|
|
53
|
+
key,
|
|
54
|
+
packMessageValue(mapValueMessage, item),
|
|
55
|
+
])
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
default:
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function packMessageValue(desc: DescMessage, value: unknown): unknown {
|
|
64
|
+
if (desc.typeName !== ANY_TYPE_NAME) {
|
|
65
|
+
return packAnyFields(desc, value);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Already a packed Any object (has typeUrl) — pass through
|
|
69
|
+
if (typeof value === 'object' && value !== null && 'typeUrl' in value) {
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (typeof value === 'string') {
|
|
74
|
+
return anyPack(StringValueSchema, create(StringValueSchema, { value }));
|
|
75
|
+
}
|
|
76
|
+
if (typeof value === 'boolean') {
|
|
77
|
+
return anyPack(BoolValueSchema, create(BoolValueSchema, { value }));
|
|
78
|
+
}
|
|
79
|
+
if (typeof value === 'number') {
|
|
80
|
+
if (Number.isInteger(value)) {
|
|
81
|
+
return anyPack(Int64ValueSchema, create(Int64ValueSchema, { value: BigInt(value) }));
|
|
82
|
+
}
|
|
83
|
+
return anyPack(DoubleValueSchema, create(DoubleValueSchema, { value }));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// create() doesn't validate an Any's shape, so passing this through would silently produce
|
|
87
|
+
// an empty Any (typeUrl: '') instead of a visible error.
|
|
88
|
+
throw new Error(
|
|
89
|
+
`packAnyFields: cannot auto-pack ${typeof value} into google.protobuf.Any — ` +
|
|
90
|
+
`expected a string, number, or boolean primitive`
|
|
91
|
+
);
|
|
92
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@michelangelo-ai/rpc",
|
|
3
3
|
"license": "Apache-2.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.10.0-rc.1",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/michelangelo-ai/michelangelo"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@bufbuild/protobuf": "^2.2.5",
|
|
20
|
-
"@michelangelo-ai/core": "0.
|
|
20
|
+
"@michelangelo-ai/core": "0.10.0-rc.1"
|
|
21
21
|
},
|
|
22
22
|
"exports": {
|
|
23
23
|
".": {
|
package/request.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { fromBinary, toJson } from '@bufbuild/protobuf';
|
|
2
|
+
|
|
3
|
+
import { TypedStructSchema } from './gen/michelangelo/api/typed_struct_pb';
|
|
1
4
|
import { getRpcHandlers } from './handlers';
|
|
2
5
|
|
|
6
|
+
import type { Any } from '@bufbuild/protobuf/wkt';
|
|
3
7
|
import type { OmitTypeName, RpcHandlerType } from './types';
|
|
4
8
|
|
|
5
9
|
/**
|
|
@@ -43,6 +47,9 @@ function toPlainObject(value: unknown): unknown {
|
|
|
43
47
|
if (value instanceof Uint8Array) return value; // preserve bytes fields (e.g. google.protobuf.Any.value)
|
|
44
48
|
if (Array.isArray(value)) return value.map(toPlainObject);
|
|
45
49
|
|
|
50
|
+
const typedStruct = unpackTypedStructAny(value);
|
|
51
|
+
if (typedStruct) return typedStruct;
|
|
52
|
+
|
|
46
53
|
const result: Record<string, unknown> = {};
|
|
47
54
|
for (const [key, val] of Object.entries(value)) {
|
|
48
55
|
if (key === '$typeName' || key === '$unknown') continue;
|
|
@@ -50,3 +57,17 @@ function toPlainObject(value: unknown): unknown {
|
|
|
50
57
|
}
|
|
51
58
|
return result;
|
|
52
59
|
}
|
|
60
|
+
|
|
61
|
+
const TYPED_STRUCT_TYPE_URL = `type.googleapis.com/${TypedStructSchema.typeName}`;
|
|
62
|
+
|
|
63
|
+
// google.protobuf.Any fields survive fromJson as { typeUrl, value: Uint8Array } — the binary
|
|
64
|
+
// payload is useless to consumers. When the payload is a michelangelo.api.TypedStruct
|
|
65
|
+
// (e.g. PipelineManifest.content), expand it to { typeUrl, value } where typeUrl names the
|
|
66
|
+
// inner config type and value is its plain JSON. Other Any payloads are left untouched.
|
|
67
|
+
function unpackTypedStructAny(value: object): unknown {
|
|
68
|
+
if (!('$typeName' in value) || value.$typeName !== 'google.protobuf.Any') return undefined;
|
|
69
|
+
// cast: the $typeName check above identifies this as a google.protobuf.Any message
|
|
70
|
+
const any = value as Any;
|
|
71
|
+
if (any.typeUrl !== TYPED_STRUCT_TYPE_URL) return undefined;
|
|
72
|
+
return toJson(TypedStructSchema, fromBinary(TypedStructSchema, any.value));
|
|
73
|
+
}
|
package/services.ts
CHANGED
|
@@ -1,20 +1,38 @@
|
|
|
1
1
|
import { create, createRegistry, fromJson, toJson } from '@bufbuild/protobuf';
|
|
2
|
+
import {
|
|
3
|
+
BoolValueSchema,
|
|
4
|
+
DoubleValueSchema,
|
|
5
|
+
Int64ValueSchema,
|
|
6
|
+
StringValueSchema,
|
|
7
|
+
} from '@bufbuild/protobuf/wkt';
|
|
2
8
|
|
|
3
9
|
import { createFetchTransport } from './create-fetch-transport';
|
|
4
10
|
import { TypedStructSchema } from './gen/michelangelo/api/typed_struct_pb';
|
|
5
11
|
import { DeploymentService } from './gen/michelangelo/api/v2/deployment_svc_pb';
|
|
6
12
|
import { InferenceServerService } from './gen/michelangelo/api/v2/inference_server_svc_pb';
|
|
13
|
+
import { ModelFamilyService } from './gen/michelangelo/api/v2/model_family_svc_pb';
|
|
7
14
|
import { ModelService } from './gen/michelangelo/api/v2/model_svc_pb';
|
|
8
15
|
import { PipelineRunService } from './gen/michelangelo/api/v2/pipeline_run_svc_pb';
|
|
9
16
|
import { PipelineService } from './gen/michelangelo/api/v2/pipeline_svc_pb';
|
|
10
17
|
import { ProjectService } from './gen/michelangelo/api/v2/project_svc_pb';
|
|
11
18
|
import { TriggerRunService } from './gen/michelangelo/api/v2/trigger_run_svc_pb';
|
|
19
|
+
import { packAnyFields } from './pack-any-fields';
|
|
12
20
|
import { getRuntimeConfig } from './runtime-config';
|
|
13
21
|
|
|
14
22
|
import type { DescService } from '@bufbuild/protobuf';
|
|
15
23
|
import type { FetchTransport, ServiceClient, Services } from './types';
|
|
16
24
|
|
|
17
|
-
|
|
25
|
+
// These wrapper schemas are registered so criteria that wrap a matchValue in a
|
|
26
|
+
// google.protobuf.Any (e.g. ListOptionsExt filters, packed by packAnyFields) can be
|
|
27
|
+
// JSON-encoded — protobuf-es resolves an Any's typeUrl against this registry to serialize
|
|
28
|
+
// it as a well-known type.
|
|
29
|
+
const typeRegistry = createRegistry(
|
|
30
|
+
TypedStructSchema,
|
|
31
|
+
StringValueSchema,
|
|
32
|
+
BoolValueSchema,
|
|
33
|
+
Int64ValueSchema,
|
|
34
|
+
DoubleValueSchema
|
|
35
|
+
);
|
|
18
36
|
|
|
19
37
|
/**
|
|
20
38
|
* Builds a service client whose methods JSON-encode the request, POST it
|
|
@@ -35,7 +53,10 @@ function createServiceClient<T extends DescService>(
|
|
|
35
53
|
if (method.methodKind !== 'unary') continue;
|
|
36
54
|
|
|
37
55
|
client[method.localName] = async (request, headers) => {
|
|
38
|
-
|
|
56
|
+
// cast: packAnyFields recurses generically over `unknown`; called with a Record it
|
|
57
|
+
// returns one, just with Any fields packed into the shape create() expects
|
|
58
|
+
const packedRequest = packAnyFields(method.input, request) as Record<string, unknown>;
|
|
59
|
+
const message = create(method.input, packedRequest);
|
|
39
60
|
const requestJson = toJson(method.input, message, { registry: typeRegistry });
|
|
40
61
|
const responseJson = await transport.callUnary(
|
|
41
62
|
service.typeName,
|
|
@@ -66,6 +87,7 @@ async function createServices(): Promise<Services> {
|
|
|
66
87
|
PipelineRunService: createServiceClient(PipelineRunService, transport),
|
|
67
88
|
TriggerRunService: createServiceClient(TriggerRunService, transport),
|
|
68
89
|
ModelService: createServiceClient(ModelService, transport),
|
|
90
|
+
ModelFamilyService: createServiceClient(ModelFamilyService, transport),
|
|
69
91
|
} as const;
|
|
70
92
|
}
|
|
71
93
|
|
package/types.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
} from '@bufbuild/protobuf';
|
|
9
9
|
import type { DeploymentService } from './gen/michelangelo/api/v2/deployment_svc_pb';
|
|
10
10
|
import type { InferenceServerService } from './gen/michelangelo/api/v2/inference_server_svc_pb';
|
|
11
|
+
import type { ModelFamilyService } from './gen/michelangelo/api/v2/model_family_svc_pb';
|
|
11
12
|
import type { ModelService } from './gen/michelangelo/api/v2/model_svc_pb';
|
|
12
13
|
import type { PipelineRunService } from './gen/michelangelo/api/v2/pipeline_run_svc_pb';
|
|
13
14
|
import type { PipelineService } from './gen/michelangelo/api/v2/pipeline_svc_pb';
|
|
@@ -67,6 +68,7 @@ export type Services = {
|
|
|
67
68
|
PipelineRunService: ServiceClient<typeof PipelineRunService>;
|
|
68
69
|
TriggerRunService: ServiceClient<typeof TriggerRunService>;
|
|
69
70
|
ModelService: ServiceClient<typeof ModelService>;
|
|
71
|
+
ModelFamilyService: ServiceClient<typeof ModelFamilyService>;
|
|
70
72
|
};
|
|
71
73
|
|
|
72
74
|
/**
|