askui 0.30.0 → 0.31.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.
Files changed (75) hide show
  1. package/dist/cjs/core/cache/cache-config.d.ts +5 -0
  2. package/dist/cjs/core/cache/cache-config.js +2 -0
  3. package/dist/cjs/core/cache/cache-entry-reference.d.ts +7 -0
  4. package/dist/cjs/core/cache/cache-entry-reference.js +20 -0
  5. package/dist/cjs/core/cache/cache-entry.d.ts +11 -0
  6. package/dist/cjs/core/cache/cache-entry.js +47 -0
  7. package/dist/cjs/core/cache/cache-interface.d.ts +11 -0
  8. package/dist/cjs/core/cache/cache-interface.js +2 -0
  9. package/dist/cjs/core/cache/cache-manager.d.ts +24 -0
  10. package/dist/cjs/core/cache/cache-manager.js +145 -0
  11. package/dist/cjs/core/cache/cahe-file.d.ts +19 -0
  12. package/dist/cjs/core/cache/cahe-file.js +128 -0
  13. package/dist/cjs/core/cache/dummy-cache-manager.d.ts +12 -0
  14. package/dist/cjs/core/cache/dummy-cache-manager.js +27 -0
  15. package/dist/cjs/core/cache/image-reference.d.ts +10 -0
  16. package/dist/cjs/core/cache/image-reference.js +40 -0
  17. package/dist/cjs/core/cache/index.d.ts +7 -0
  18. package/dist/cjs/core/cache/index.js +13 -0
  19. package/dist/cjs/core/model/annotation-result/boundary-box.js +1 -1
  20. package/dist/cjs/core/model/custom-element.d.ts +1 -0
  21. package/dist/cjs/core/model/custom-element.js +3 -0
  22. package/dist/cjs/core/ui-control-commands/action.d.ts +1 -0
  23. package/dist/cjs/core/ui-control-commands/action.js +10 -2
  24. package/dist/cjs/core/ui-control-commands/control-command.d.ts +1 -0
  25. package/dist/cjs/core/ui-control-commands/control-command.js +7 -0
  26. package/dist/cjs/execution/dsl.d.ts +12 -5
  27. package/dist/cjs/execution/dsl.js +30 -15
  28. package/dist/cjs/execution/execution-runtime.d.ts +1 -1
  29. package/dist/cjs/execution/execution-runtime.js +21 -17
  30. package/dist/cjs/execution/inference-client.d.ts +5 -3
  31. package/dist/cjs/execution/inference-client.js +22 -3
  32. package/dist/cjs/execution/ui-control-client-dependency-builder.js +6 -1
  33. package/dist/cjs/execution/ui-control-client.d.ts +2 -1
  34. package/dist/cjs/execution/ui-control-client.js +28 -11
  35. package/dist/cjs/execution/ui-controller-client-interface.d.ts +2 -0
  36. package/dist/cjs/utils/base_64_image/base-64-image.d.ts +2 -0
  37. package/dist/cjs/utils/base_64_image/base-64-image.js +27 -4
  38. package/dist/esm/core/cache/cache-config.d.ts +5 -0
  39. package/dist/esm/core/cache/cache-config.js +1 -0
  40. package/dist/esm/core/cache/cache-entry-reference.d.ts +7 -0
  41. package/dist/esm/core/cache/cache-entry-reference.js +16 -0
  42. package/dist/esm/core/cache/cache-entry.d.ts +11 -0
  43. package/dist/esm/core/cache/cache-entry.js +43 -0
  44. package/dist/esm/core/cache/cache-interface.d.ts +11 -0
  45. package/dist/esm/core/cache/cache-interface.js +1 -0
  46. package/dist/esm/core/cache/cache-manager.d.ts +24 -0
  47. package/dist/esm/core/cache/cache-manager.js +141 -0
  48. package/dist/esm/core/cache/cahe-file.d.ts +19 -0
  49. package/dist/esm/core/cache/cahe-file.js +121 -0
  50. package/dist/esm/core/cache/dummy-cache-manager.d.ts +12 -0
  51. package/dist/esm/core/cache/dummy-cache-manager.js +23 -0
  52. package/dist/esm/core/cache/image-reference.d.ts +10 -0
  53. package/dist/esm/core/cache/image-reference.js +36 -0
  54. package/dist/esm/core/cache/index.d.ts +7 -0
  55. package/dist/esm/core/cache/index.js +5 -0
  56. package/dist/esm/core/model/annotation-result/boundary-box.js +1 -1
  57. package/dist/esm/core/model/custom-element.d.ts +1 -0
  58. package/dist/esm/core/model/custom-element.js +3 -0
  59. package/dist/esm/core/ui-control-commands/action.d.ts +1 -0
  60. package/dist/esm/core/ui-control-commands/action.js +10 -2
  61. package/dist/esm/core/ui-control-commands/control-command.d.ts +1 -0
  62. package/dist/esm/core/ui-control-commands/control-command.js +7 -0
  63. package/dist/esm/execution/dsl.d.ts +12 -5
  64. package/dist/esm/execution/dsl.js +30 -15
  65. package/dist/esm/execution/execution-runtime.d.ts +1 -1
  66. package/dist/esm/execution/execution-runtime.js +21 -17
  67. package/dist/esm/execution/inference-client.d.ts +5 -3
  68. package/dist/esm/execution/inference-client.js +22 -3
  69. package/dist/esm/execution/ui-control-client-dependency-builder.js +6 -1
  70. package/dist/esm/execution/ui-control-client.d.ts +2 -1
  71. package/dist/esm/execution/ui-control-client.js +28 -11
  72. package/dist/esm/execution/ui-controller-client-interface.d.ts +2 -0
  73. package/dist/esm/utils/base_64_image/base-64-image.d.ts +2 -0
  74. package/dist/esm/utils/base_64_image/base-64-image.js +27 -4
  75. package/package.json +1 -1
@@ -3,6 +3,15 @@
3
3
  /* eslint-disable max-classes-per-file */
4
4
  /* eslint-disable max-len */
5
5
  // Autogenerated from typescript.template file
6
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
7
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
8
+ return new (P || (P = Promise))(function (resolve, reject) {
9
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
10
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
11
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
12
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
13
+ });
14
+ };
6
15
  function isStackTraceCodeline(line) {
7
16
  return /[ \t]+at .+/.test(line);
8
17
  }
@@ -14,21 +23,18 @@ function splitStackTrace(stacktrace) {
14
23
  return { head: errorStacktraceHead, codelines: errorStacktraceCodeLines };
15
24
  }
16
25
  function rewriteStackTraceForError(error, newStackTrace) {
17
- const errorCopy = new Error(error.message);
18
- if (!error.stack) {
19
- errorCopy.stack = newStackTrace;
20
- return errorCopy;
21
- }
22
- const errorStacktraceSplit = splitStackTrace(error.stack);
26
+ var _a;
27
+ const errorStacktraceSplit = splitStackTrace((_a = error.stack) !== null && _a !== void 0 ? _a : '');
23
28
  const newStacktraceSplit = splitStackTrace(newStackTrace);
24
- errorCopy.stack = [
29
+ // eslint-disable-next-line no-param-reassign
30
+ error.stack = [
25
31
  ...errorStacktraceSplit.head,
26
32
  ...newStacktraceSplit.codelines,
27
33
  ' ',
28
34
  ...errorStacktraceSplit.head,
29
35
  ...errorStacktraceSplit.codelines,
30
36
  ].join('\n');
31
- return errorCopy;
37
+ return error;
32
38
  }
33
39
  export var Separators;
34
40
  (function (Separators) {
@@ -52,7 +58,7 @@ class FluentBase {
52
58
  });
53
59
  return paramsList;
54
60
  }
55
- fluentCommandStringBuilder(modelComposition, currentInstruction = '', paramsList = new Map()) {
61
+ fluentCommandStringBuilder(modelComposition = [], skipCache = false, retryStrategy, currentInstruction = '', paramsList = new Map()) {
56
62
  const newCurrentInstruction = `${this.textStr} ${currentInstruction}`;
57
63
  const newParamsList = FluentBase.addParams(paramsList, this._params);
58
64
  if (this instanceof FluentCommand) {
@@ -62,12 +68,12 @@ class FluentBase {
62
68
  return fluentCommand.fluentCommandExecutor(newCurrentInstruction.trim(), modelComposition, {
63
69
  customElementsJson: customElements,
64
70
  aiElementNames,
65
- });
71
+ }, skipCache, retryStrategy);
66
72
  }
67
73
  if (!this.prev) {
68
74
  throw new Error('Prev element not defined');
69
75
  }
70
- return this.prev.fluentCommandStringBuilder(modelComposition, newCurrentInstruction, newParamsList);
76
+ return this.prev.fluentCommandStringBuilder(modelComposition, skipCache, retryStrategy, newCurrentInstruction, newParamsList);
71
77
  }
72
78
  getterStringBuilder(currentInstruction = '', paramsList = new Map()) {
73
79
  const newCurrentInstruction = `${this.textStr} ${currentInstruction}`;
@@ -90,10 +96,10 @@ class FluentBase {
90
96
  get params() { return this._params; }
91
97
  }
92
98
  export class Exec extends FluentBase {
93
- exec(modelComposition = []) {
99
+ exec(execOptions) {
94
100
  const originStacktrace = { stack: '' };
95
101
  Error.captureStackTrace(originStacktrace, this.exec);
96
- return this.fluentCommandStringBuilder(modelComposition).catch((err) => Promise.reject(rewriteStackTraceForError(err, originStacktrace.stack)));
102
+ return this.fluentCommandStringBuilder(execOptions === null || execOptions === void 0 ? void 0 : execOptions.modelComposition, execOptions === null || execOptions === void 0 ? void 0 : execOptions.skipCache, execOptions === null || execOptions === void 0 ? void 0 : execOptions.retryStrategy).catch((err) => Promise.reject(rewriteStackTraceForError(err, originStacktrace.stack)));
97
103
  }
98
104
  }
99
105
  // Filters
@@ -1043,10 +1049,10 @@ export class FluentFiltersOrRelations extends FluentFilters {
1043
1049
  this._textStr += 'contains';
1044
1050
  return new FluentFilters(this);
1045
1051
  }
1046
- exec(modelComposition = []) {
1052
+ exec(execOptions) {
1047
1053
  const originStacktrace = { stack: '' };
1048
1054
  Error.captureStackTrace(originStacktrace, this.exec);
1049
- return this.fluentCommandStringBuilder(modelComposition).catch((err) => Promise.reject(rewriteStackTraceForError(err, originStacktrace.stack)));
1055
+ return this.fluentCommandStringBuilder(execOptions === null || execOptions === void 0 ? void 0 : execOptions.modelComposition, execOptions === null || execOptions === void 0 ? void 0 : execOptions.skipCache, execOptions === null || execOptions === void 0 ? void 0 : execOptions.retryStrategy).catch((err) => Promise.reject(rewriteStackTraceForError(err, originStacktrace.stack)));
1050
1056
  }
1051
1057
  }
1052
1058
  // Filters
@@ -2055,6 +2061,15 @@ export class FluentFiltersOrRelationsCondition extends FluentFiltersCondition {
2055
2061
  }
2056
2062
  }
2057
2063
  class ExecCondition extends Exec {
2064
+ exec(execOptions) {
2065
+ const _super = Object.create(null, {
2066
+ exec: { get: () => super.exec }
2067
+ });
2068
+ return __awaiter(this, void 0, void 0, function* () {
2069
+ const options = Object.assign(Object.assign({}, execOptions), { skipCache: true });
2070
+ return _super.exec.call(this, options);
2071
+ });
2072
+ }
2058
2073
  }
2059
2074
  // Commands
2060
2075
  export class FluentCommand extends FluentBase {
@@ -21,7 +21,7 @@ export declare class ExecutionRuntime {
21
21
  stopVideoRecording(): Promise<void>;
22
22
  readVideoRecording(): Promise<string>;
23
23
  requestControl(controlCommand: ControlCommand): Promise<void>;
24
- executeInstruction(instruction: Instruction, modelComposition: ModelCompositionBranch[]): Promise<void>;
24
+ executeInstruction(instruction: Instruction, modelComposition: ModelCompositionBranch[], skipCache?: boolean, retryStrategy?: RetryStrategy): Promise<void>;
25
25
  private readonly EXEC_REPETITION_COUNT;
26
26
  private executeCommandRepeatedly;
27
27
  /**
@@ -25,10 +25,12 @@ export class ExecutionRuntime {
25
25
  }
26
26
  connect() {
27
27
  return __awaiter(this, void 0, void 0, function* () {
28
+ this.inferenceClient.cacheManager.loadFromFile();
28
29
  return this.uiControllerClient.connect();
29
30
  });
30
31
  }
31
32
  disconnect() {
33
+ this.inferenceClient.cacheManager.saveToFile();
32
34
  this.uiControllerClient.disconnect();
33
35
  }
34
36
  startVideoRecording() {
@@ -52,10 +54,10 @@ export class ExecutionRuntime {
52
54
  yield this.uiControllerClient.requestControl(controlCommand);
53
55
  });
54
56
  }
55
- executeInstruction(instruction, modelComposition) {
56
- return __awaiter(this, void 0, void 0, function* () {
57
+ executeInstruction(instruction_1, modelComposition_1) {
58
+ return __awaiter(this, arguments, void 0, function* (instruction, modelComposition, skipCache = false, retryStrategy) {
57
59
  var _a, _b, _c;
58
- const controlCommand = yield this.predictCommandWithRetry(instruction, modelComposition);
60
+ const controlCommand = yield this.predictCommandWithRetry(instruction, modelComposition, skipCache, retryStrategy);
59
61
  if (controlCommand.code === ControlCommandCode.OK) {
60
62
  return this.requestControl(controlCommand);
61
63
  }
@@ -95,19 +97,20 @@ export class ExecutionRuntime {
95
97
  * --> retry with linear back-off
96
98
  */
97
99
  /* eslint-disable-next-line consistent-return */
98
- predictCommandWithRetry(instruction, modelComposition) {
99
- return __awaiter(this, void 0, void 0, function* () {
100
+ predictCommandWithRetry(instruction_1, modelComposition_1) {
101
+ return __awaiter(this, arguments, void 0, function* (instruction, modelComposition, skipCache = false, retryStrategy) {
100
102
  var _a, _b, _c;
101
- let command = yield this.predictCommand(instruction, modelComposition);
103
+ const strategy = retryStrategy !== null && retryStrategy !== void 0 ? retryStrategy : this.retryStrategy;
104
+ let command = yield this.predictCommand(instruction, modelComposition, skipCache);
102
105
  /* eslint-disable no-await-in-loop */
103
- for (let k = 0; k < this.retryStrategy.retryCount; k += 1) {
106
+ for (let k = 0; k < strategy.retryCount; k += 1) {
104
107
  if (command.code === ControlCommandCode.OK) {
105
108
  return command;
106
109
  }
107
- const msUntilRetry = this.retryStrategy.getDelay(k + 1);
108
- logger.debug(`Wait ${msUntilRetry} and retry predicting command...`);
110
+ const msUntilRetry = strategy.getDelay(k + 1);
111
+ logger.debug(`Wait ${msUntilRetry} and retry ${k + 1}/${strategy.retryCount} predicting command...`);
109
112
  yield delay(msUntilRetry);
110
- command = yield this.predictCommand(instruction, modelComposition, new ControlCommandError((_c = (_b = (_a = command.actions) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.text) !== null && _c !== void 0 ? _c : ''));
113
+ command = yield this.predictCommand(instruction, modelComposition, skipCache, new ControlCommandError((_c = (_b = (_a = command.actions) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.text) !== null && _c !== void 0 ? _c : ''));
111
114
  }
112
115
  /* eslint-enable no-await-in-loop */
113
116
  return command;
@@ -117,11 +120,11 @@ export class ExecutionRuntime {
117
120
  return this.stepReporter.config.withScreenshots === 'begin'
118
121
  || this.stepReporter.config.withScreenshots === 'always';
119
122
  }
120
- isImageRequired(instruction) {
123
+ isImageRequired(instruction, customElements) {
121
124
  return __awaiter(this, void 0, void 0, function* () {
122
125
  if (this.isImageRequiredByConfig())
123
126
  return Promise.resolve(true);
124
- return this.inferenceClient.isImageRequired(instruction);
127
+ return this.inferenceClient.isImageRequired(instruction, customElements);
125
128
  });
126
129
  }
127
130
  isAnnotationRequired() {
@@ -136,8 +139,9 @@ export class ExecutionRuntime {
136
139
  }
137
140
  buildSnapshot(instruction) {
138
141
  return __awaiter(this, void 0, void 0, function* () {
142
+ var _a;
139
143
  const createdAt = new Date();
140
- const screenshot = (yield this.isImageRequired(instruction))
144
+ const screenshot = (yield this.isImageRequired(instruction.value, (_a = instruction.customElements) !== null && _a !== void 0 ? _a : []))
141
145
  ? yield this.getScreenshot() : undefined;
142
146
  const annotation = this.isAnnotationRequired() ? yield this.annotateImage() : undefined;
143
147
  return {
@@ -147,14 +151,14 @@ export class ExecutionRuntime {
147
151
  };
148
152
  });
149
153
  }
150
- predictCommand(instruction, modelComposition, retryError) {
151
- return __awaiter(this, void 0, void 0, function* () {
152
- const snapshot = yield this.buildSnapshot(instruction.value);
154
+ predictCommand(instruction_1, modelComposition_1) {
155
+ return __awaiter(this, arguments, void 0, function* (instruction, modelComposition, skipCache = false, retryError) {
156
+ const snapshot = yield this.buildSnapshot(instruction);
153
157
  if (retryError !== undefined)
154
158
  this.stepReporter.onStepRetry(snapshot, retryError);
155
159
  else
156
160
  this.stepReporter.onStepBegin(snapshot);
157
- const controlCommand = yield this.inferenceClient.predictControlCommand(instruction.value, modelComposition, instruction.customElements, snapshot.screenshot);
161
+ const controlCommand = yield this.inferenceClient.predictControlCommand(instruction.value, modelComposition, instruction.customElements, snapshot.screenshot, skipCache);
158
162
  if (instruction.secretText !== undefined) {
159
163
  controlCommand.setTextToBeTyped(instruction.secretText);
160
164
  }
@@ -6,21 +6,23 @@ import { Annotation } from '../core/annotation/annotation';
6
6
  import { DetectedElement } from '../core/model/annotation-result/detected-element';
7
7
  import { VQAInferenceResponseBody } from '../core/inference-response/inference-response';
8
8
  import { ModelCompositionBranch } from './model-composition-branch';
9
+ import { CacheInterface } from '../core/cache';
9
10
  export declare class InferenceClient {
10
11
  private readonly baseUrl;
11
12
  private readonly httpClient;
13
+ readonly cacheManager: CacheInterface;
12
14
  private readonly resize?;
13
15
  readonly workspaceId?: string | undefined;
14
16
  readonly modelComposition?: ModelCompositionBranch[] | undefined;
15
17
  private readonly apiVersion;
16
18
  private urls;
17
- constructor(baseUrl: string, httpClient: HttpClientGot, resize?: number | undefined, workspaceId?: string | undefined, modelComposition?: ModelCompositionBranch[] | undefined, apiVersion?: string);
18
- isImageRequired(instruction: string): Promise<boolean>;
19
+ constructor(baseUrl: string, httpClient: HttpClientGot, cacheManager: CacheInterface, resize?: number | undefined, workspaceId?: string | undefined, modelComposition?: ModelCompositionBranch[] | undefined, apiVersion?: string);
20
+ isImageRequired(instruction: string, customElements: CustomElement[]): Promise<boolean>;
19
21
  private resizeIfNeeded;
20
22
  inference(customElements?: CustomElement[], image?: string, instruction?: string, modelComposition?: ModelCompositionBranch[]): Promise<ControlCommand | Annotation>;
21
23
  vqaInference(image: string, prompt: string, config?: object): Promise<VQAInferenceResponseBody>;
22
24
  private static logMetaInformation;
23
- predictControlCommand(instruction: string, modelComposition: ModelCompositionBranch[], customElements?: CustomElement[], image?: string): Promise<ControlCommand>;
25
+ predictControlCommand(instruction: string, modelComposition: ModelCompositionBranch[], customElements?: CustomElement[], image?: string, skipCache?: boolean): Promise<ControlCommand>;
24
26
  getDetectedElements(instruction: string, image: string, customElements?: CustomElement[]): Promise<DetectedElement[]>;
25
27
  predictImageAnnotation(image: string, customElements?: CustomElement[]): Promise<Annotation>;
26
28
  predictVQAAnswer(prompt: string, image: string, config?: object): Promise<any>;
@@ -14,10 +14,12 @@ import { resizeBase64ImageWithSameRatio } from '../utils/transformations';
14
14
  import { InferenceResponseError } from './inference-response-error';
15
15
  import { ConfigurationError } from './config-error';
16
16
  import { logger } from '../lib/logger';
17
+ import { ControlCommandCode } from '../core/ui-control-commands/control-command-code';
17
18
  export class InferenceClient {
18
- constructor(baseUrl, httpClient, resize, workspaceId, modelComposition, apiVersion = 'v1') {
19
+ constructor(baseUrl, httpClient, cacheManager, resize, workspaceId, modelComposition, apiVersion = 'v1') {
19
20
  this.baseUrl = baseUrl;
20
21
  this.httpClient = httpClient;
22
+ this.cacheManager = cacheManager;
21
23
  this.resize = resize;
22
24
  this.workspaceId = workspaceId;
23
25
  this.modelComposition = modelComposition;
@@ -38,8 +40,13 @@ export class InferenceClient {
38
40
  }
39
41
  this.resize = this.resize ? Math.ceil(this.resize) : this.resize;
40
42
  }
41
- isImageRequired(instruction) {
43
+ isImageRequired(instruction, customElements) {
42
44
  return __awaiter(this, void 0, void 0, function* () {
45
+ const cachedImageRequired = this.cacheManager.isImageRequired(instruction, customElements);
46
+ if (cachedImageRequired !== undefined) {
47
+ logger.debug(`Cache hit for image required: '${instruction}'.`);
48
+ return Promise.resolve(cachedImageRequired);
49
+ }
43
50
  const response = yield this.httpClient.post(this.urls.isImageRequired, {
44
51
  instruction,
45
52
  });
@@ -90,11 +97,23 @@ export class InferenceClient {
90
97
  }
91
98
  }
92
99
  predictControlCommand(instruction_1, modelComposition_1) {
93
- return __awaiter(this, arguments, void 0, function* (instruction, modelComposition, customElements = [], image) {
100
+ return __awaiter(this, arguments, void 0, function* (instruction, modelComposition, customElements = [], image, skipCache = false) {
101
+ if (!skipCache) {
102
+ const cachedControlCommand = yield this.cacheManager.getCachedControlCommand(instruction, customElements, image);
103
+ if (cachedControlCommand !== undefined) {
104
+ logger.debug(`Cache hit for instruction: '${instruction}'.`);
105
+ return Promise.resolve(cachedControlCommand);
106
+ }
107
+ logger.debug(`Cache miss for instruction: '${instruction}'.`);
108
+ }
94
109
  const inferenceResponse = yield this.inference(customElements, image, instruction, modelComposition);
95
110
  if (!(inferenceResponse instanceof ControlCommand)) {
96
111
  throw new InferenceResponseError('Internal Error. Can not execute command');
97
112
  }
113
+ if (!skipCache && inferenceResponse.code === ControlCommandCode.OK) {
114
+ yield this.cacheManager.addCacheEntryFromControlCommand(instruction, inferenceResponse, customElements, image);
115
+ logger.debug(`Cache added for instruction: '${instruction}'`);
116
+ }
98
117
  return inferenceResponse;
99
118
  });
100
119
  }
@@ -17,6 +17,7 @@ import { ExecutionRuntime } from './execution-runtime';
17
17
  import { StepReporter } from '../core/reporting';
18
18
  import { readCredentials } from './read-credentials';
19
19
  import { LinearRetryStrategy } from './retry-strategies/linear-retry-strategy';
20
+ import { CacheManager, DummyCacheManager } from '../core/cache';
20
21
  export class UiControlClientDependencyBuilder {
21
22
  static buildHttpClient(clientArgs) {
22
23
  return __awaiter(this, void 0, void 0, function* () {
@@ -31,7 +32,11 @@ export class UiControlClientDependencyBuilder {
31
32
  return __awaiter(this, void 0, void 0, function* () {
32
33
  var _a;
33
34
  const httpClient = yield UiControlClientDependencyBuilder.buildHttpClient(clientArgs);
34
- return new InferenceClient(clientArgs.inferenceServerUrl, httpClient, clientArgs.resize, (_a = clientArgs.credentials) === null || _a === void 0 ? void 0 : _a.workspaceId, clientArgs.modelComposition, clientArgs.inferenceServerApiVersion);
35
+ let cacheManager = new DummyCacheManager();
36
+ if (clientArgs.cacheConfig) {
37
+ cacheManager = new CacheManager(clientArgs.cacheConfig);
38
+ }
39
+ return new InferenceClient(clientArgs.inferenceServerUrl, httpClient, cacheManager, clientArgs.resize, (_a = clientArgs.credentials) === null || _a === void 0 ? void 0 : _a.workspaceId, clientArgs.modelComposition, clientArgs.inferenceServerApiVersion);
35
40
  });
36
41
  }
37
42
  static buildUiControllerClient(clientArgs) {
@@ -5,6 +5,7 @@ import { AnnotationRequest } from '../core/model/annotation-result/annotation-in
5
5
  import { DetectedElement } from '../core/model/annotation-result/detected-element';
6
6
  import { ClientArgs } from './ui-controller-client-interface';
7
7
  import { ModelCompositionBranch } from './model-composition-branch';
8
+ import { RetryStrategy } from './retry-strategies';
8
9
  import { AskUIAgent, AgentHistory, ActOptions } from '../core/models/anthropic';
9
10
  export type RelationsForConvenienceMethods = 'nearestTo' | 'leftOf' | 'above' | 'rightOf' | 'below' | 'contains';
10
11
  export type TextMatchingOption = 'similar' | 'exact' | 'regex';
@@ -62,7 +63,7 @@ export declare class UiControlClient extends ApiCommands {
62
63
  private escapeSeparatorString;
63
64
  private buildInstruction;
64
65
  private getAIElementsByNames;
65
- fluentCommandExecutor(instructionString: string, modelComposition: ModelCompositionBranch[], context?: CommandExecutorContext): Promise<void>;
66
+ fluentCommandExecutor(instructionString: string, modelComposition: ModelCompositionBranch[], context?: CommandExecutorContext, skipCache?: boolean, retryStrategy?: RetryStrategy): Promise<void>;
66
67
  getterExecutor(instruction: string, context?: CommandExecutorContext): Promise<DetectedElement[]>;
67
68
  /**
68
69
  * Takes a prompt that contains a question you want to be answered
@@ -15,6 +15,7 @@ import { logger } from '../lib/logger';
15
15
  import { UiControlClientDependencyBuilder } from './ui-control-client-dependency-builder';
16
16
  import { AIElementCollection } from '../core/ai-element/ai-element-collection';
17
17
  import { NoRetryStrategy } from './retry-strategies';
18
+ import { ControlCommandError } from './control-command-error';
18
19
  import { AskUIAgent } from '../core/models/anthropic';
19
20
  import { AskUIGetAskUIElementTool, AskUIListAIElementTool } from '../core/models/anthropic/tools/askui-api-tools';
20
21
  export class UiControlClient extends ApiCommands {
@@ -157,7 +158,7 @@ export class UiControlClient extends ApiCommands {
157
158
  });
158
159
  }
159
160
  fluentCommandExecutor(instructionString_1, modelComposition_1) {
160
- return __awaiter(this, arguments, void 0, function* (instructionString, modelComposition, context = { customElementsJson: [], aiElementNames: [] }) {
161
+ return __awaiter(this, arguments, void 0, function* (instructionString, modelComposition, context = { customElementsJson: [], aiElementNames: [] }, skipCache = false, retryStrategy) {
161
162
  const aiElements = yield this.getAIElementsByNames(context.aiElementNames);
162
163
  const instruction = yield this.buildInstruction(instructionString, [
163
164
  ...context.customElementsJson,
@@ -166,7 +167,7 @@ export class UiControlClient extends ApiCommands {
166
167
  logger.debug(instruction);
167
168
  try {
168
169
  this.stepReporter.resetStep(instruction);
169
- yield this.executionRuntime.executeInstruction(instruction, modelComposition);
170
+ yield this.executionRuntime.executeInstruction(instruction, modelComposition, skipCache, retryStrategy);
170
171
  yield this.afterCommandExecution(instruction);
171
172
  return yield Promise.resolve();
172
173
  }
@@ -450,18 +451,34 @@ export class UiControlClient extends ApiCommands {
450
451
  */
451
452
  waitUntil(AskUICommand_1) {
452
453
  return __awaiter(this, arguments, void 0, function* (AskUICommand, maxTry = 5, waitTime = 2000) {
454
+ logger.debug(`waitUntil: Starting with maxTry=${maxTry}, waitTime=${waitTime}ms, retryStrategy=${this.executionRuntime.retryStrategy.constructor.name}`);
453
455
  const userDefinedStrategy = this.executionRuntime.retryStrategy;
454
- try {
455
- this.executionRuntime.retryStrategy = new NoRetryStrategy();
456
- yield AskUICommand.exec();
457
- this.executionRuntime.retryStrategy = userDefinedStrategy;
458
- }
459
- catch (error) {
460
- if (maxTry === 0) {
456
+ this.executionRuntime.retryStrategy = new NoRetryStrategy();
457
+ const attempt = (retriesLeft) => __awaiter(this, void 0, void 0, function* () {
458
+ const attemptNumber = maxTry - retriesLeft;
459
+ logger.debug(`waitUntil: Attempt ${attemptNumber}/${maxTry} (${retriesLeft} retries remaining)`);
460
+ try {
461
+ yield AskUICommand.exec();
462
+ logger.debug(`waitUntil: Command succeeded on attempt ${attemptNumber}/${maxTry}`);
463
+ return;
464
+ }
465
+ catch (error) {
466
+ if (error instanceof ControlCommandError && retriesLeft > 0) {
467
+ logger.debug(`waitUntil: ControlCommandError on attempt ${attemptNumber}/${maxTry}, waiting ${waitTime}ms before retry.`, error);
468
+ yield this.waitFor(waitTime).exec();
469
+ yield attempt(retriesLeft - 1);
470
+ return;
471
+ }
472
+ const errorName = error instanceof Error ? error.name : 'Error';
473
+ logger.debug(`waitUntil: ${errorName} on attempt ${attemptNumber}/${maxTry}, no retries remaining.`, error);
461
474
  throw error;
462
475
  }
463
- yield this.waitFor(waitTime).exec();
464
- yield this.waitUntil(AskUICommand, maxTry - 1, waitTime);
476
+ });
477
+ try {
478
+ yield attempt(maxTry - 1);
479
+ }
480
+ finally {
481
+ this.executionRuntime.retryStrategy = userDefinedStrategy;
465
482
  }
466
483
  });
467
484
  }
@@ -5,6 +5,7 @@ import { Reporter } from '../core/reporting';
5
5
  import { Context } from './context';
6
6
  import { RetryStrategy } from './retry-strategies/retry-strategy';
7
7
  import { AIElementArgs } from '../core/ai-element/ai-elements-args';
8
+ import { CacheConfig } from '../core/cache';
8
9
  /**
9
10
  * Context object to provide additional information about the context of (test) automation.
10
11
  *
@@ -63,6 +64,7 @@ export interface ClientArgs {
63
64
  readonly inferenceServerApiVersion?: string;
64
65
  readonly retryStrategy?: RetryStrategy;
65
66
  readonly aiElementArgs?: AIElementArgs;
67
+ readonly cacheConfig?: CacheConfig;
66
68
  }
67
69
  export interface ClientArgsWithDefaults extends ClientArgs {
68
70
  readonly uiControllerUrl: string;
@@ -12,5 +12,7 @@ export declare class Base64Image {
12
12
  getInfo(): Promise<sharp.OutputInfo>;
13
13
  resizeToFitInto(dimension: number): Promise<Base64Image>;
14
14
  resizeWithSameAspectRatio(width: number, height: number): Promise<Base64Image>;
15
+ cropRegion(x: number, y: number, croppedWidth: number, croppedHeight: number): Promise<Base64Image>;
15
16
  toString(withPrefix?: boolean): string;
17
+ toBuffer(): Buffer;
16
18
  }
@@ -61,11 +61,12 @@ export class Base64Image {
61
61
  resizeToFitInto(dimension) {
62
62
  return __awaiter(this, void 0, void 0, function* () {
63
63
  const { width, height } = yield this.getInfo();
64
+ const roundedDimension = Math.round(dimension);
64
65
  const buffer = yield (yield this.getSharp())
65
66
  .resize({
66
67
  fit: 'contain',
67
- height: height > width ? dimension : undefined,
68
- width: width >= height ? dimension : undefined,
68
+ height: height > width ? roundedDimension : undefined,
69
+ width: width >= height ? roundedDimension : undefined,
69
70
  })
70
71
  .toBuffer();
71
72
  return Base64Image.fromBuffer(buffer);
@@ -73,11 +74,30 @@ export class Base64Image {
73
74
  }
74
75
  resizeWithSameAspectRatio(width, height) {
75
76
  return __awaiter(this, void 0, void 0, function* () {
77
+ const roundedWidth = Math.round(width);
78
+ const roundedHeight = Math.round(height);
76
79
  const buffer = yield (yield this.getSharp())
77
80
  .resize({
78
81
  fit: 'contain',
79
- height,
80
- width,
82
+ height: roundedHeight,
83
+ width: roundedWidth,
84
+ })
85
+ .toBuffer();
86
+ return Base64Image.fromBuffer(buffer);
87
+ });
88
+ }
89
+ cropRegion(x, y, croppedWidth, croppedHeight) {
90
+ return __awaiter(this, void 0, void 0, function* () {
91
+ const roundedX = Math.round(x);
92
+ const roundedY = Math.round(y);
93
+ const roundedCroppedWidth = Math.round(croppedWidth);
94
+ const roundedCroppedHeight = Math.round(croppedHeight);
95
+ const buffer = yield (yield this.getSharp())
96
+ .extract({
97
+ height: roundedCroppedHeight,
98
+ left: roundedX,
99
+ top: roundedY,
100
+ width: roundedCroppedWidth,
81
101
  })
82
102
  .toBuffer();
83
103
  return Base64Image.fromBuffer(buffer);
@@ -89,5 +109,8 @@ export class Base64Image {
89
109
  }
90
110
  return this.buffer.toString('base64');
91
111
  }
112
+ toBuffer() {
113
+ return this.buffer;
114
+ }
92
115
  }
93
116
  Base64Image.strPrefix = 'data:image/png;base64,';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "askui",
3
- "version": "0.30.0",
3
+ "version": "0.31.0",
4
4
  "license": "MIT",
5
5
  "author": "askui GmbH <info@askui.com> (http://www.askui.com/)",
6
6
  "description": "Reliable, automated end-to-end-testing that depends on what is shown on your screen instead of the technology you are running on",