@hautechai/sdk 0.1.4 → 0.2.0
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/.github/workflows/main.yml +31 -31
- package/.github/workflows/test.yml +30 -30
- package/.prettierignore +1 -0
- package/dist/autogenerated/api.d.ts +981 -23
- package/dist/autogenerated/api.js +1110 -161
- package/dist/sdk/api.d.ts +2 -0
- package/dist/sdk/api.js +10 -10
- package/dist/sdk/exceptions/index.d.ts +3 -0
- package/dist/sdk/exceptions/index.js +6 -0
- package/dist/sdk/index.d.ts +763 -142
- package/dist/sdk/index.js +18 -1
- package/dist/sdk/listeners/index.d.ts +25 -0
- package/dist/sdk/listeners/index.js +81 -0
- package/dist/sdk/operations/index.d.ts +3 -2
- package/dist/sdk/operations/index.js +18 -14
- package/dist/sdk/pipelines/index.d.ts +760 -140
- package/dist/sdk/pipelines/index.js +59 -2
- package/dist/sdk/pipelines/pipelines.d.ts +0 -0
- package/dist/sdk/pipelines/pipelines.js +1 -0
- package/dist/token/index.js +32 -21
- package/dist/types.d.ts +1 -0
- package/openapitools.json +5 -5
- package/package.json +5 -3
package/dist/sdk/index.js
CHANGED
|
@@ -10,6 +10,9 @@ import pipelines from './pipelines';
|
|
|
10
10
|
import stacks from './stacks';
|
|
11
11
|
import storage from './storage';
|
|
12
12
|
import utils from './utils';
|
|
13
|
+
import { OperationsListener } from './listeners';
|
|
14
|
+
import { getAPI, getBaseUrl } from './api';
|
|
15
|
+
import { OperationsApi } from '../autogenerated';
|
|
13
16
|
export const createSDK = (options) => {
|
|
14
17
|
let token = undefined;
|
|
15
18
|
const authToken = async () => {
|
|
@@ -23,6 +26,17 @@ export const createSDK = (options) => {
|
|
|
23
26
|
return token;
|
|
24
27
|
};
|
|
25
28
|
const optionsWithTokenRefresher = { ...options, authToken };
|
|
29
|
+
const operationsListener = new OperationsListener({
|
|
30
|
+
ws: options.useWebsocket
|
|
31
|
+
? {
|
|
32
|
+
endpoint: getBaseUrl(options),
|
|
33
|
+
token: authToken,
|
|
34
|
+
}
|
|
35
|
+
: null,
|
|
36
|
+
// TODO: Refactor the API initialization
|
|
37
|
+
operations: () => getAPI(OperationsApi, optionsWithTokenRefresher),
|
|
38
|
+
});
|
|
39
|
+
operationsListener.subscribe();
|
|
26
40
|
return {
|
|
27
41
|
access: access(optionsWithTokenRefresher),
|
|
28
42
|
accounts: accounts(optionsWithTokenRefresher),
|
|
@@ -30,10 +44,13 @@ export const createSDK = (options) => {
|
|
|
30
44
|
collections: collections(optionsWithTokenRefresher),
|
|
31
45
|
groups: groups(optionsWithTokenRefresher),
|
|
32
46
|
images: images(optionsWithTokenRefresher),
|
|
33
|
-
operations: operations(optionsWithTokenRefresher),
|
|
47
|
+
operations: operations(optionsWithTokenRefresher, operationsListener),
|
|
34
48
|
pipelines: pipelines(optionsWithTokenRefresher),
|
|
35
49
|
stacks: stacks(optionsWithTokenRefresher),
|
|
36
50
|
storage: storage(optionsWithTokenRefresher),
|
|
37
51
|
utils: utils(optionsWithTokenRefresher),
|
|
52
|
+
close: () => {
|
|
53
|
+
operationsListener.close();
|
|
54
|
+
},
|
|
38
55
|
};
|
|
39
56
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { OperationEntity, OperationsApi } from '../../autogenerated';
|
|
2
|
+
import { WebSocket } from 'ws';
|
|
3
|
+
export declare class OperationsListener {
|
|
4
|
+
useWebsocket: {
|
|
5
|
+
endpoint: string;
|
|
6
|
+
token: () => string | Promise<string>;
|
|
7
|
+
} | null;
|
|
8
|
+
ws: WebSocket | null;
|
|
9
|
+
operations: () => Promise<OperationsApi>;
|
|
10
|
+
constructor({ ws, operations, }: {
|
|
11
|
+
ws: {
|
|
12
|
+
endpoint: string;
|
|
13
|
+
token: () => string | Promise<string>;
|
|
14
|
+
} | null;
|
|
15
|
+
operations: () => Promise<OperationsApi>;
|
|
16
|
+
});
|
|
17
|
+
operationsStore: Record<string, OperationEntity>;
|
|
18
|
+
getOperation(id: string): Promise<OperationEntity | null>;
|
|
19
|
+
private updateOperation;
|
|
20
|
+
subscribe(): Promise<void>;
|
|
21
|
+
onOpen(): void;
|
|
22
|
+
onMessage(msg: string): void;
|
|
23
|
+
onClose(number: number, reason: Buffer): void;
|
|
24
|
+
close(): void;
|
|
25
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { WebSocket } from 'ws';
|
|
2
|
+
import { HautechException } from '../exceptions';
|
|
3
|
+
// This is pretty much a dirty solution until we need to rework this part.
|
|
4
|
+
export class OperationsListener {
|
|
5
|
+
constructor({ ws, operations, }) {
|
|
6
|
+
this.useWebsocket = null;
|
|
7
|
+
this.ws = null;
|
|
8
|
+
this.operationsStore = {};
|
|
9
|
+
if (ws) {
|
|
10
|
+
this.useWebsocket = {
|
|
11
|
+
endpoint: ws?.endpoint,
|
|
12
|
+
token: ws?.token,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
this.operations = operations;
|
|
16
|
+
}
|
|
17
|
+
async getOperation(id) {
|
|
18
|
+
const isWsReady = this.ws?.readyState == WebSocket.OPEN;
|
|
19
|
+
if (!this.operationsStore[id] || !isWsReady) {
|
|
20
|
+
const api = await this.operations();
|
|
21
|
+
const operation = await api.operationsControllerGetOperationV1(id);
|
|
22
|
+
if (operation.status == 200)
|
|
23
|
+
this.updateOperation(id, operation.data);
|
|
24
|
+
}
|
|
25
|
+
return this.operationsStore[id] || null;
|
|
26
|
+
}
|
|
27
|
+
updateOperation(id, operation) {
|
|
28
|
+
const stored = this.operationsStore[id];
|
|
29
|
+
if (!stored || stored.updatedAt < operation.updatedAt) {
|
|
30
|
+
this.operationsStore[id] = operation;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async subscribe() {
|
|
34
|
+
if (!this.useWebsocket)
|
|
35
|
+
return;
|
|
36
|
+
try {
|
|
37
|
+
this.ws = new WebSocket(this.useWebsocket.endpoint, ['1', await this.useWebsocket.token()]);
|
|
38
|
+
this.ws.on('open', () => this.onOpen());
|
|
39
|
+
this.ws.on('message', (msg) => this.onMessage(msg));
|
|
40
|
+
this.ws.on('close', (number, reason) => this.onClose(number, reason));
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
throw new HautechException(`SDK failed to open websocket: ${err}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
onOpen() {
|
|
47
|
+
if (!this.ws)
|
|
48
|
+
throw new HautechException('Semantics error: this is a bug.');
|
|
49
|
+
this.ws.send(JSON.stringify({
|
|
50
|
+
event: 'subscribe',
|
|
51
|
+
data: {
|
|
52
|
+
channel: 'own_resources',
|
|
53
|
+
},
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
onMessage(msg) {
|
|
57
|
+
if (!this.ws)
|
|
58
|
+
throw new HautechException('Semantics error: this is a bug.');
|
|
59
|
+
const { event, data } = JSON.parse(msg);
|
|
60
|
+
switch (event) {
|
|
61
|
+
case 'subscription:created':
|
|
62
|
+
break;
|
|
63
|
+
case 'operation:created':
|
|
64
|
+
case 'operation:updated':
|
|
65
|
+
this.updateOperation(data.id, data);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
onClose(number, reason) {
|
|
70
|
+
if (!this.ws)
|
|
71
|
+
throw new HautechException('Semantics error: this is a bug.');
|
|
72
|
+
// Reset dirty state.
|
|
73
|
+
this.operationsStore = {};
|
|
74
|
+
this.ws = null;
|
|
75
|
+
}
|
|
76
|
+
close() {
|
|
77
|
+
if (!this.ws)
|
|
78
|
+
return;
|
|
79
|
+
this.ws.close();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CompositeV1Input, CompositeV1Response, CutV1Input, CutV1Response, GiseleVtonV1Input, GPTV1Input, GptV1Response, HauteLindaV1Response, HauteNaomiV1Response, ImagineKateV1Response, KateImagineV1Input, KateInpaintV1Input, LindaHauteV1Input, NaomiHauteV1Input, NegateImageV1Input, NegateImageV1Response, ObjectDetectionV1Input, ObjectDetectionV1Response, OperationEntity, OperationEntityStatusEnum, PoseEstimationV1Input, PoseEstimationV1Response, SegmentAnythingEmbeddingsV1Input, SegmentAnythingEmbeddingsV1Response, SegmentAnythingMaskV1Input, SegmentAnythingMaskV1Response, UpscaleV1Input, UpscaleV1Response, VtonGiseleV1Response } from '../../autogenerated';
|
|
2
2
|
import { ListProps, ListResponse, SDKOptions } from '../../types';
|
|
3
|
+
import { OperationsListener } from '../listeners';
|
|
3
4
|
type Waited<T extends OperationEntity> = T & ({
|
|
4
5
|
status: typeof OperationEntityStatusEnum.Failed;
|
|
5
6
|
output: null;
|
|
@@ -10,7 +11,7 @@ type Waited<T extends OperationEntity> = T & ({
|
|
|
10
11
|
status: typeof OperationEntityStatusEnum.Finished;
|
|
11
12
|
output: NonNullable<T['output']>;
|
|
12
13
|
});
|
|
13
|
-
declare const operations: (options: SDKOptions) => {
|
|
14
|
+
declare const operations: (options: SDKOptions, relevantOperations: OperationsListener) => {
|
|
14
15
|
run: {
|
|
15
16
|
haute: {
|
|
16
17
|
linda: {
|
|
@@ -120,6 +121,6 @@ declare const operations: (options: SDKOptions) => {
|
|
|
120
121
|
}) => Promise<void>;
|
|
121
122
|
wait: <T extends OperationEntity | {
|
|
122
123
|
id: string;
|
|
123
|
-
}>(props: T) => Promise<Waited<T extends OperationEntity ? T : OperationEntity
|
|
124
|
+
}>(props: T, timeoutMs?: number) => Promise<Waited<T extends OperationEntity ? T : OperationEntity> | null>;
|
|
124
125
|
};
|
|
125
126
|
export default operations;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { OperationsApi, } from '../../autogenerated';
|
|
2
2
|
import { useAutogeneratedAPI } from '../api';
|
|
3
3
|
import { transformToListResponse } from '../transformers';
|
|
4
|
-
const
|
|
4
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
5
|
+
const operations = (options, relevantOperations) => {
|
|
5
6
|
const api = useAutogeneratedAPI({ API: OperationsApi, options });
|
|
6
7
|
const createOperation = (callMethod) => (props) => api.call({ run: (methods) => callMethod(methods, props) });
|
|
7
8
|
return {
|
|
@@ -72,20 +73,23 @@ const operations = (options) => {
|
|
|
72
73
|
updateMetadata: async (props) => api.call({
|
|
73
74
|
run: (methods) => methods.operationsControllerUpdateMetadataV1(props.id, { overwrite: props.metadata }),
|
|
74
75
|
}),
|
|
75
|
-
wait: async (props
|
|
76
|
-
const
|
|
77
|
-
const delay =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (operation.status !== 'pending')
|
|
84
|
-
return resolve(operation);
|
|
85
|
-
timeoutId = setTimeout(poll, delay);
|
|
76
|
+
wait: async (props, timeoutMs) => {
|
|
77
|
+
const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
|
|
78
|
+
const delay = 1000;
|
|
79
|
+
const poll = async (id) => {
|
|
80
|
+
const operation = await relevantOperations.getOperation(id);
|
|
81
|
+
if (operation?.status !== 'pending')
|
|
82
|
+
return operation;
|
|
83
|
+
return null;
|
|
86
84
|
};
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
while (!deadline || Date.now() < deadline) {
|
|
86
|
+
const polled = await poll(props.id);
|
|
87
|
+
if (polled)
|
|
88
|
+
return polled;
|
|
89
|
+
await sleep(delay);
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
},
|
|
89
93
|
};
|
|
90
94
|
};
|
|
91
95
|
export default operations;
|