askui 0.30.0 → 0.32.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 (79) 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/cjs/utils/http/http-client-got.d.ts +15 -0
  39. package/dist/cjs/utils/http/http-client-got.js +59 -11
  40. package/dist/esm/core/cache/cache-config.d.ts +5 -0
  41. package/dist/esm/core/cache/cache-config.js +1 -0
  42. package/dist/esm/core/cache/cache-entry-reference.d.ts +7 -0
  43. package/dist/esm/core/cache/cache-entry-reference.js +16 -0
  44. package/dist/esm/core/cache/cache-entry.d.ts +11 -0
  45. package/dist/esm/core/cache/cache-entry.js +43 -0
  46. package/dist/esm/core/cache/cache-interface.d.ts +11 -0
  47. package/dist/esm/core/cache/cache-interface.js +1 -0
  48. package/dist/esm/core/cache/cache-manager.d.ts +24 -0
  49. package/dist/esm/core/cache/cache-manager.js +141 -0
  50. package/dist/esm/core/cache/cahe-file.d.ts +19 -0
  51. package/dist/esm/core/cache/cahe-file.js +121 -0
  52. package/dist/esm/core/cache/dummy-cache-manager.d.ts +12 -0
  53. package/dist/esm/core/cache/dummy-cache-manager.js +23 -0
  54. package/dist/esm/core/cache/image-reference.d.ts +10 -0
  55. package/dist/esm/core/cache/image-reference.js +36 -0
  56. package/dist/esm/core/cache/index.d.ts +7 -0
  57. package/dist/esm/core/cache/index.js +5 -0
  58. package/dist/esm/core/model/annotation-result/boundary-box.js +1 -1
  59. package/dist/esm/core/model/custom-element.d.ts +1 -0
  60. package/dist/esm/core/model/custom-element.js +3 -0
  61. package/dist/esm/core/ui-control-commands/action.d.ts +1 -0
  62. package/dist/esm/core/ui-control-commands/action.js +10 -2
  63. package/dist/esm/core/ui-control-commands/control-command.d.ts +1 -0
  64. package/dist/esm/core/ui-control-commands/control-command.js +7 -0
  65. package/dist/esm/execution/dsl.d.ts +12 -5
  66. package/dist/esm/execution/dsl.js +30 -15
  67. package/dist/esm/execution/execution-runtime.d.ts +1 -1
  68. package/dist/esm/execution/execution-runtime.js +21 -17
  69. package/dist/esm/execution/inference-client.d.ts +5 -3
  70. package/dist/esm/execution/inference-client.js +22 -3
  71. package/dist/esm/execution/ui-control-client-dependency-builder.js +6 -1
  72. package/dist/esm/execution/ui-control-client.d.ts +2 -1
  73. package/dist/esm/execution/ui-control-client.js +28 -11
  74. package/dist/esm/execution/ui-controller-client-interface.d.ts +2 -0
  75. package/dist/esm/utils/base_64_image/base-64-image.d.ts +2 -0
  76. package/dist/esm/utils/base_64_image/base-64-image.js +27 -4
  77. package/dist/esm/utils/http/http-client-got.d.ts +15 -0
  78. package/dist/esm/utils/http/http-client-got.js +36 -8
  79. package/package.json +4 -2
@@ -1,6 +1,7 @@
1
1
  import { CustomElementJson } from '../core/model/custom-element-json';
2
2
  import { DetectedElement } from '../core/model/annotation-result/detected-element';
3
3
  import { ModelCompositionBranch } from './model-composition-branch';
4
+ import { RetryStrategy } from './retry-strategies/retry-strategy';
4
5
  export declare enum Separators {
5
6
  STRING = "<|string|>"
6
7
  }
@@ -17,22 +18,27 @@ export interface CommandExecutorContext {
17
18
  customElementsJson: CustomElementJson[];
18
19
  aiElementNames: string[];
19
20
  }
21
+ export interface ExecOptions {
22
+ modelComposition?: ModelCompositionBranch[];
23
+ skipCache?: boolean;
24
+ retryStrategy?: RetryStrategy;
25
+ }
20
26
  declare abstract class FluentBase {
21
27
  protected prev?: FluentBase | undefined;
22
28
  constructor(prev?: FluentBase | undefined);
23
29
  protected _textStr: string;
24
30
  protected _params: Map<string, unknown>;
25
31
  protected static addParams(paramsList: Map<string, unknown[]>, params: Map<string, unknown>): Map<string, unknown[]>;
26
- protected fluentCommandStringBuilder(modelComposition: ModelCompositionBranch[], currentInstruction?: string, paramsList?: Map<string, unknown[]>): Promise<void>;
32
+ protected fluentCommandStringBuilder(modelComposition?: ModelCompositionBranch[], skipCache?: boolean, retryStrategy?: RetryStrategy, currentInstruction?: string, paramsList?: Map<string, unknown[]>): Promise<void>;
27
33
  protected getterStringBuilder(currentInstruction?: string, paramsList?: Map<string, unknown[]>): Promise<DetectedElement[]>;
28
34
  protected get textStr(): string;
29
35
  protected get params(): Map<string, unknown>;
30
36
  }
31
37
  export interface Executable {
32
- exec(): Promise<void>;
38
+ exec(execOptions?: ExecOptions): Promise<void>;
33
39
  }
34
40
  export declare class Exec extends FluentBase implements Executable {
35
- exec(modelComposition?: ModelCompositionBranch[]): Promise<void>;
41
+ exec(execOptions?: ExecOptions): Promise<void>;
36
42
  }
37
43
  export declare class FluentFilters extends FluentBase {
38
44
  /**
@@ -808,7 +814,7 @@ export declare class FluentFiltersOrRelations extends FluentFilters {
808
814
  * @return {FluentFilters}
809
815
  */
810
816
  contains(): FluentFilters;
811
- exec(modelComposition?: ModelCompositionBranch[]): Promise<void>;
817
+ exec(execOptions?: ExecOptions): Promise<void>;
812
818
  }
813
819
  export declare class FluentFiltersCondition extends FluentBase {
814
820
  /**
@@ -1634,6 +1640,7 @@ export declare class FluentFiltersOrRelationsCondition extends FluentFiltersCond
1634
1640
  notExists(): ExecCondition;
1635
1641
  }
1636
1642
  declare class ExecCondition extends Exec {
1643
+ exec(execOptions?: ExecOptions): Promise<void>;
1637
1644
  }
1638
1645
  export declare abstract class FluentCommand extends FluentBase {
1639
1646
  constructor();
@@ -2082,7 +2089,7 @@ export declare abstract class FluentCommand extends FluentBase {
2082
2089
  * @return {Exec}
2083
2090
  */
2084
2091
  pressAndroidKey(key: ANDROID_KEY): Exec;
2085
- abstract fluentCommandExecutor(instruction: string, modelComposition: ModelCompositionBranch[], context: CommandExecutorContext): Promise<void>;
2092
+ abstract fluentCommandExecutor(instruction: string, modelComposition: ModelCompositionBranch[], context: CommandExecutorContext, skipCache: boolean, retryStrategy?: RetryStrategy): Promise<void>;
2086
2093
  }
2087
2094
  export interface ExecutableGetter {
2088
2095
  exec(): Promise<DetectedElement[]>;
@@ -4,6 +4,15 @@
4
4
  /* eslint-disable max-classes-per-file */
5
5
  /* eslint-disable max-len */
6
6
  // Autogenerated from typescript.template file
7
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
8
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
9
+ return new (P || (P = Promise))(function (resolve, reject) {
10
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
11
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
12
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
13
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
14
+ });
15
+ };
7
16
  Object.defineProperty(exports, "__esModule", { value: true });
8
17
  exports.ApiCommands = exports.Getter = exports.FluentFiltersOrRelationsGetter = exports.FluentFiltersGetter = exports.ExecGetter = exports.FluentCommand = exports.FluentFiltersOrRelationsCondition = exports.FluentFiltersCondition = exports.FluentFiltersOrRelations = exports.FluentFilters = exports.Exec = exports.MODIFIER_KEY_VALUES = exports.ANDROID_KEY_VALUES = exports.PC_KEY_VALUES = exports.Separators = void 0;
9
18
  function isStackTraceCodeline(line) {
@@ -17,21 +26,18 @@ function splitStackTrace(stacktrace) {
17
26
  return { head: errorStacktraceHead, codelines: errorStacktraceCodeLines };
18
27
  }
19
28
  function rewriteStackTraceForError(error, newStackTrace) {
20
- const errorCopy = new Error(error.message);
21
- if (!error.stack) {
22
- errorCopy.stack = newStackTrace;
23
- return errorCopy;
24
- }
25
- const errorStacktraceSplit = splitStackTrace(error.stack);
29
+ var _a;
30
+ const errorStacktraceSplit = splitStackTrace((_a = error.stack) !== null && _a !== void 0 ? _a : '');
26
31
  const newStacktraceSplit = splitStackTrace(newStackTrace);
27
- errorCopy.stack = [
32
+ // eslint-disable-next-line no-param-reassign
33
+ error.stack = [
28
34
  ...errorStacktraceSplit.head,
29
35
  ...newStacktraceSplit.codelines,
30
36
  ' ',
31
37
  ...errorStacktraceSplit.head,
32
38
  ...errorStacktraceSplit.codelines,
33
39
  ].join('\n');
34
- return errorCopy;
40
+ return error;
35
41
  }
36
42
  var Separators;
37
43
  (function (Separators) {
@@ -55,7 +61,7 @@ class FluentBase {
55
61
  });
56
62
  return paramsList;
57
63
  }
58
- fluentCommandStringBuilder(modelComposition, currentInstruction = '', paramsList = new Map()) {
64
+ fluentCommandStringBuilder(modelComposition = [], skipCache = false, retryStrategy, currentInstruction = '', paramsList = new Map()) {
59
65
  const newCurrentInstruction = `${this.textStr} ${currentInstruction}`;
60
66
  const newParamsList = FluentBase.addParams(paramsList, this._params);
61
67
  if (this instanceof FluentCommand) {
@@ -65,12 +71,12 @@ class FluentBase {
65
71
  return fluentCommand.fluentCommandExecutor(newCurrentInstruction.trim(), modelComposition, {
66
72
  customElementsJson: customElements,
67
73
  aiElementNames,
68
- });
74
+ }, skipCache, retryStrategy);
69
75
  }
70
76
  if (!this.prev) {
71
77
  throw new Error('Prev element not defined');
72
78
  }
73
- return this.prev.fluentCommandStringBuilder(modelComposition, newCurrentInstruction, newParamsList);
79
+ return this.prev.fluentCommandStringBuilder(modelComposition, skipCache, retryStrategy, newCurrentInstruction, newParamsList);
74
80
  }
75
81
  getterStringBuilder(currentInstruction = '', paramsList = new Map()) {
76
82
  const newCurrentInstruction = `${this.textStr} ${currentInstruction}`;
@@ -93,10 +99,10 @@ class FluentBase {
93
99
  get params() { return this._params; }
94
100
  }
95
101
  class Exec extends FluentBase {
96
- exec(modelComposition = []) {
102
+ exec(execOptions) {
97
103
  const originStacktrace = { stack: '' };
98
104
  Error.captureStackTrace(originStacktrace, this.exec);
99
- return this.fluentCommandStringBuilder(modelComposition).catch((err) => Promise.reject(rewriteStackTraceForError(err, originStacktrace.stack)));
105
+ 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)));
100
106
  }
101
107
  }
102
108
  exports.Exec = Exec;
@@ -1048,10 +1054,10 @@ class FluentFiltersOrRelations extends FluentFilters {
1048
1054
  this._textStr += 'contains';
1049
1055
  return new FluentFilters(this);
1050
1056
  }
1051
- exec(modelComposition = []) {
1057
+ exec(execOptions) {
1052
1058
  const originStacktrace = { stack: '' };
1053
1059
  Error.captureStackTrace(originStacktrace, this.exec);
1054
- return this.fluentCommandStringBuilder(modelComposition).catch((err) => Promise.reject(rewriteStackTraceForError(err, originStacktrace.stack)));
1060
+ 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)));
1055
1061
  }
1056
1062
  }
1057
1063
  exports.FluentFiltersOrRelations = FluentFiltersOrRelations;
@@ -2063,6 +2069,15 @@ class FluentFiltersOrRelationsCondition extends FluentFiltersCondition {
2063
2069
  }
2064
2070
  exports.FluentFiltersOrRelationsCondition = FluentFiltersOrRelationsCondition;
2065
2071
  class ExecCondition extends Exec {
2072
+ exec(execOptions) {
2073
+ const _super = Object.create(null, {
2074
+ exec: { get: () => super.exec }
2075
+ });
2076
+ return __awaiter(this, void 0, void 0, function* () {
2077
+ const options = Object.assign(Object.assign({}, execOptions), { skipCache: true });
2078
+ return _super.exec.call(this, options);
2079
+ });
2080
+ }
2066
2081
  }
2067
2082
  // Commands
2068
2083
  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
  /**
@@ -28,10 +28,12 @@ class ExecutionRuntime {
28
28
  }
29
29
  connect() {
30
30
  return __awaiter(this, void 0, void 0, function* () {
31
+ this.inferenceClient.cacheManager.loadFromFile();
31
32
  return this.uiControllerClient.connect();
32
33
  });
33
34
  }
34
35
  disconnect() {
36
+ this.inferenceClient.cacheManager.saveToFile();
35
37
  this.uiControllerClient.disconnect();
36
38
  }
37
39
  startVideoRecording() {
@@ -55,10 +57,10 @@ class ExecutionRuntime {
55
57
  yield this.uiControllerClient.requestControl(controlCommand);
56
58
  });
57
59
  }
58
- executeInstruction(instruction, modelComposition) {
59
- return __awaiter(this, void 0, void 0, function* () {
60
+ executeInstruction(instruction_1, modelComposition_1) {
61
+ return __awaiter(this, arguments, void 0, function* (instruction, modelComposition, skipCache = false, retryStrategy) {
60
62
  var _a, _b, _c;
61
- const controlCommand = yield this.predictCommandWithRetry(instruction, modelComposition);
63
+ const controlCommand = yield this.predictCommandWithRetry(instruction, modelComposition, skipCache, retryStrategy);
62
64
  if (controlCommand.code === ui_control_commands_1.ControlCommandCode.OK) {
63
65
  return this.requestControl(controlCommand);
64
66
  }
@@ -98,19 +100,20 @@ class ExecutionRuntime {
98
100
  * --> retry with linear back-off
99
101
  */
100
102
  /* eslint-disable-next-line consistent-return */
101
- predictCommandWithRetry(instruction, modelComposition) {
102
- return __awaiter(this, void 0, void 0, function* () {
103
+ predictCommandWithRetry(instruction_1, modelComposition_1) {
104
+ return __awaiter(this, arguments, void 0, function* (instruction, modelComposition, skipCache = false, retryStrategy) {
103
105
  var _a, _b, _c;
104
- let command = yield this.predictCommand(instruction, modelComposition);
106
+ const strategy = retryStrategy !== null && retryStrategy !== void 0 ? retryStrategy : this.retryStrategy;
107
+ let command = yield this.predictCommand(instruction, modelComposition, skipCache);
105
108
  /* eslint-disable no-await-in-loop */
106
- for (let k = 0; k < this.retryStrategy.retryCount; k += 1) {
109
+ for (let k = 0; k < strategy.retryCount; k += 1) {
107
110
  if (command.code === ui_control_commands_1.ControlCommandCode.OK) {
108
111
  return command;
109
112
  }
110
- const msUntilRetry = this.retryStrategy.getDelay(k + 1);
111
- logger_1.logger.debug(`Wait ${msUntilRetry} and retry predicting command...`);
113
+ const msUntilRetry = strategy.getDelay(k + 1);
114
+ logger_1.logger.debug(`Wait ${msUntilRetry} and retry ${k + 1}/${strategy.retryCount} predicting command...`);
112
115
  yield (0, misc_1.delay)(msUntilRetry);
113
- command = yield this.predictCommand(instruction, modelComposition, new control_command_error_1.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 : ''));
116
+ command = yield this.predictCommand(instruction, modelComposition, skipCache, new control_command_error_1.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 : ''));
114
117
  }
115
118
  /* eslint-enable no-await-in-loop */
116
119
  return command;
@@ -120,11 +123,11 @@ class ExecutionRuntime {
120
123
  return this.stepReporter.config.withScreenshots === 'begin'
121
124
  || this.stepReporter.config.withScreenshots === 'always';
122
125
  }
123
- isImageRequired(instruction) {
126
+ isImageRequired(instruction, customElements) {
124
127
  return __awaiter(this, void 0, void 0, function* () {
125
128
  if (this.isImageRequiredByConfig())
126
129
  return Promise.resolve(true);
127
- return this.inferenceClient.isImageRequired(instruction);
130
+ return this.inferenceClient.isImageRequired(instruction, customElements);
128
131
  });
129
132
  }
130
133
  isAnnotationRequired() {
@@ -139,8 +142,9 @@ class ExecutionRuntime {
139
142
  }
140
143
  buildSnapshot(instruction) {
141
144
  return __awaiter(this, void 0, void 0, function* () {
145
+ var _a;
142
146
  const createdAt = new Date();
143
- const screenshot = (yield this.isImageRequired(instruction))
147
+ const screenshot = (yield this.isImageRequired(instruction.value, (_a = instruction.customElements) !== null && _a !== void 0 ? _a : []))
144
148
  ? yield this.getScreenshot() : undefined;
145
149
  const annotation = this.isAnnotationRequired() ? yield this.annotateImage() : undefined;
146
150
  return {
@@ -150,14 +154,14 @@ class ExecutionRuntime {
150
154
  };
151
155
  });
152
156
  }
153
- predictCommand(instruction, modelComposition, retryError) {
154
- return __awaiter(this, void 0, void 0, function* () {
155
- const snapshot = yield this.buildSnapshot(instruction.value);
157
+ predictCommand(instruction_1, modelComposition_1) {
158
+ return __awaiter(this, arguments, void 0, function* (instruction, modelComposition, skipCache = false, retryError) {
159
+ const snapshot = yield this.buildSnapshot(instruction);
156
160
  if (retryError !== undefined)
157
161
  this.stepReporter.onStepRetry(snapshot, retryError);
158
162
  else
159
163
  this.stepReporter.onStepBegin(snapshot);
160
- const controlCommand = yield this.inferenceClient.predictControlCommand(instruction.value, modelComposition, instruction.customElements, snapshot.screenshot);
164
+ const controlCommand = yield this.inferenceClient.predictControlCommand(instruction.value, modelComposition, instruction.customElements, snapshot.screenshot, skipCache);
161
165
  if (instruction.secretText !== undefined) {
162
166
  controlCommand.setTextToBeTyped(instruction.secretText);
163
167
  }
@@ -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>;
@@ -20,10 +20,12 @@ const transformations_1 = require("../utils/transformations");
20
20
  const inference_response_error_1 = require("./inference-response-error");
21
21
  const config_error_1 = require("./config-error");
22
22
  const logger_1 = require("../lib/logger");
23
+ const control_command_code_1 = require("../core/ui-control-commands/control-command-code");
23
24
  class InferenceClient {
24
- constructor(baseUrl, httpClient, resize, workspaceId, modelComposition, apiVersion = 'v1') {
25
+ constructor(baseUrl, httpClient, cacheManager, resize, workspaceId, modelComposition, apiVersion = 'v1') {
25
26
  this.baseUrl = baseUrl;
26
27
  this.httpClient = httpClient;
28
+ this.cacheManager = cacheManager;
27
29
  this.resize = resize;
28
30
  this.workspaceId = workspaceId;
29
31
  this.modelComposition = modelComposition;
@@ -44,8 +46,13 @@ class InferenceClient {
44
46
  }
45
47
  this.resize = this.resize ? Math.ceil(this.resize) : this.resize;
46
48
  }
47
- isImageRequired(instruction) {
49
+ isImageRequired(instruction, customElements) {
48
50
  return __awaiter(this, void 0, void 0, function* () {
51
+ const cachedImageRequired = this.cacheManager.isImageRequired(instruction, customElements);
52
+ if (cachedImageRequired !== undefined) {
53
+ logger_1.logger.debug(`Cache hit for image required: '${instruction}'.`);
54
+ return Promise.resolve(cachedImageRequired);
55
+ }
49
56
  const response = yield this.httpClient.post(this.urls.isImageRequired, {
50
57
  instruction,
51
58
  });
@@ -96,11 +103,23 @@ class InferenceClient {
96
103
  }
97
104
  }
98
105
  predictControlCommand(instruction_1, modelComposition_1) {
99
- return __awaiter(this, arguments, void 0, function* (instruction, modelComposition, customElements = [], image) {
106
+ return __awaiter(this, arguments, void 0, function* (instruction, modelComposition, customElements = [], image, skipCache = false) {
107
+ if (!skipCache) {
108
+ const cachedControlCommand = yield this.cacheManager.getCachedControlCommand(instruction, customElements, image);
109
+ if (cachedControlCommand !== undefined) {
110
+ logger_1.logger.debug(`Cache hit for instruction: '${instruction}'.`);
111
+ return Promise.resolve(cachedControlCommand);
112
+ }
113
+ logger_1.logger.debug(`Cache miss for instruction: '${instruction}'.`);
114
+ }
100
115
  const inferenceResponse = yield this.inference(customElements, image, instruction, modelComposition);
101
116
  if (!(inferenceResponse instanceof ui_control_commands_1.ControlCommand)) {
102
117
  throw new inference_response_error_1.InferenceResponseError('Internal Error. Can not execute command');
103
118
  }
119
+ if (!skipCache && inferenceResponse.code === control_command_code_1.ControlCommandCode.OK) {
120
+ yield this.cacheManager.addCacheEntryFromControlCommand(instruction, inferenceResponse, customElements, image);
121
+ logger_1.logger.debug(`Cache added for instruction: '${instruction}'`);
122
+ }
104
123
  return inferenceResponse;
105
124
  });
106
125
  }
@@ -23,6 +23,7 @@ const execution_runtime_1 = require("./execution-runtime");
23
23
  const reporting_1 = require("../core/reporting");
24
24
  const read_credentials_1 = require("./read-credentials");
25
25
  const linear_retry_strategy_1 = require("./retry-strategies/linear-retry-strategy");
26
+ const cache_1 = require("../core/cache");
26
27
  class UiControlClientDependencyBuilder {
27
28
  static buildHttpClient(clientArgs) {
28
29
  return __awaiter(this, void 0, void 0, function* () {
@@ -37,7 +38,11 @@ class UiControlClientDependencyBuilder {
37
38
  return __awaiter(this, void 0, void 0, function* () {
38
39
  var _a;
39
40
  const httpClient = yield UiControlClientDependencyBuilder.buildHttpClient(clientArgs);
40
- return new inference_client_1.InferenceClient(clientArgs.inferenceServerUrl, httpClient, clientArgs.resize, (_a = clientArgs.credentials) === null || _a === void 0 ? void 0 : _a.workspaceId, clientArgs.modelComposition, clientArgs.inferenceServerApiVersion);
41
+ let cacheManager = new cache_1.DummyCacheManager();
42
+ if (clientArgs.cacheConfig) {
43
+ cacheManager = new cache_1.CacheManager(clientArgs.cacheConfig);
44
+ }
45
+ return new inference_client_1.InferenceClient(clientArgs.inferenceServerUrl, httpClient, cacheManager, clientArgs.resize, (_a = clientArgs.credentials) === null || _a === void 0 ? void 0 : _a.workspaceId, clientArgs.modelComposition, clientArgs.inferenceServerApiVersion);
41
46
  });
42
47
  }
43
48
  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
@@ -18,6 +18,7 @@ const logger_1 = require("../lib/logger");
18
18
  const ui_control_client_dependency_builder_1 = require("./ui-control-client-dependency-builder");
19
19
  const ai_element_collection_1 = require("../core/ai-element/ai-element-collection");
20
20
  const retry_strategies_1 = require("./retry-strategies");
21
+ const control_command_error_1 = require("./control-command-error");
21
22
  const anthropic_1 = require("../core/models/anthropic");
22
23
  const askui_api_tools_1 = require("../core/models/anthropic/tools/askui-api-tools");
23
24
  class UiControlClient extends dsl_1.ApiCommands {
@@ -160,7 +161,7 @@ class UiControlClient extends dsl_1.ApiCommands {
160
161
  });
161
162
  }
162
163
  fluentCommandExecutor(instructionString_1, modelComposition_1) {
163
- return __awaiter(this, arguments, void 0, function* (instructionString, modelComposition, context = { customElementsJson: [], aiElementNames: [] }) {
164
+ return __awaiter(this, arguments, void 0, function* (instructionString, modelComposition, context = { customElementsJson: [], aiElementNames: [] }, skipCache = false, retryStrategy) {
164
165
  const aiElements = yield this.getAIElementsByNames(context.aiElementNames);
165
166
  const instruction = yield this.buildInstruction(instructionString, [
166
167
  ...context.customElementsJson,
@@ -169,7 +170,7 @@ class UiControlClient extends dsl_1.ApiCommands {
169
170
  logger_1.logger.debug(instruction);
170
171
  try {
171
172
  this.stepReporter.resetStep(instruction);
172
- yield this.executionRuntime.executeInstruction(instruction, modelComposition);
173
+ yield this.executionRuntime.executeInstruction(instruction, modelComposition, skipCache, retryStrategy);
173
174
  yield this.afterCommandExecution(instruction);
174
175
  return yield Promise.resolve();
175
176
  }
@@ -453,18 +454,34 @@ class UiControlClient extends dsl_1.ApiCommands {
453
454
  */
454
455
  waitUntil(AskUICommand_1) {
455
456
  return __awaiter(this, arguments, void 0, function* (AskUICommand, maxTry = 5, waitTime = 2000) {
457
+ logger_1.logger.debug(`waitUntil: Starting with maxTry=${maxTry}, waitTime=${waitTime}ms, retryStrategy=${this.executionRuntime.retryStrategy.constructor.name}`);
456
458
  const userDefinedStrategy = this.executionRuntime.retryStrategy;
457
- try {
458
- this.executionRuntime.retryStrategy = new retry_strategies_1.NoRetryStrategy();
459
- yield AskUICommand.exec();
460
- this.executionRuntime.retryStrategy = userDefinedStrategy;
461
- }
462
- catch (error) {
463
- if (maxTry === 0) {
459
+ this.executionRuntime.retryStrategy = new retry_strategies_1.NoRetryStrategy();
460
+ const attempt = (retriesLeft) => __awaiter(this, void 0, void 0, function* () {
461
+ const attemptNumber = maxTry - retriesLeft;
462
+ logger_1.logger.debug(`waitUntil: Attempt ${attemptNumber}/${maxTry} (${retriesLeft} retries remaining)`);
463
+ try {
464
+ yield AskUICommand.exec();
465
+ logger_1.logger.debug(`waitUntil: Command succeeded on attempt ${attemptNumber}/${maxTry}`);
466
+ return;
467
+ }
468
+ catch (error) {
469
+ if (error instanceof control_command_error_1.ControlCommandError && retriesLeft > 0) {
470
+ logger_1.logger.debug(`waitUntil: ControlCommandError on attempt ${attemptNumber}/${maxTry}, waiting ${waitTime}ms before retry.`, error);
471
+ yield this.waitFor(waitTime).exec();
472
+ yield attempt(retriesLeft - 1);
473
+ return;
474
+ }
475
+ const errorName = error instanceof Error ? error.name : 'Error';
476
+ logger_1.logger.debug(`waitUntil: ${errorName} on attempt ${attemptNumber}/${maxTry}, no retries remaining.`, error);
464
477
  throw error;
465
478
  }
466
- yield this.waitFor(waitTime).exec();
467
- yield this.waitUntil(AskUICommand, maxTry - 1, waitTime);
479
+ });
480
+ try {
481
+ yield attempt(maxTry - 1);
482
+ }
483
+ finally {
484
+ this.executionRuntime.retryStrategy = userDefinedStrategy;
468
485
  }
469
486
  });
470
487
  }
@@ -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
  }
@@ -67,11 +67,12 @@ class Base64Image {
67
67
  resizeToFitInto(dimension) {
68
68
  return __awaiter(this, void 0, void 0, function* () {
69
69
  const { width, height } = yield this.getInfo();
70
+ const roundedDimension = Math.round(dimension);
70
71
  const buffer = yield (yield this.getSharp())
71
72
  .resize({
72
73
  fit: 'contain',
73
- height: height > width ? dimension : undefined,
74
- width: width >= height ? dimension : undefined,
74
+ height: height > width ? roundedDimension : undefined,
75
+ width: width >= height ? roundedDimension : undefined,
75
76
  })
76
77
  .toBuffer();
77
78
  return Base64Image.fromBuffer(buffer);
@@ -79,11 +80,30 @@ class Base64Image {
79
80
  }
80
81
  resizeWithSameAspectRatio(width, height) {
81
82
  return __awaiter(this, void 0, void 0, function* () {
83
+ const roundedWidth = Math.round(width);
84
+ const roundedHeight = Math.round(height);
82
85
  const buffer = yield (yield this.getSharp())
83
86
  .resize({
84
87
  fit: 'contain',
85
- height,
86
- width,
88
+ height: roundedHeight,
89
+ width: roundedWidth,
90
+ })
91
+ .toBuffer();
92
+ return Base64Image.fromBuffer(buffer);
93
+ });
94
+ }
95
+ cropRegion(x, y, croppedWidth, croppedHeight) {
96
+ return __awaiter(this, void 0, void 0, function* () {
97
+ const roundedX = Math.round(x);
98
+ const roundedY = Math.round(y);
99
+ const roundedCroppedWidth = Math.round(croppedWidth);
100
+ const roundedCroppedHeight = Math.round(croppedHeight);
101
+ const buffer = yield (yield this.getSharp())
102
+ .extract({
103
+ height: roundedCroppedHeight,
104
+ left: roundedX,
105
+ top: roundedY,
106
+ width: roundedCroppedWidth,
87
107
  })
88
108
  .toBuffer();
89
109
  return Base64Image.fromBuffer(buffer);
@@ -95,6 +115,9 @@ class Base64Image {
95
115
  }
96
116
  return this.buffer.toString('base64');
97
117
  }
118
+ toBuffer() {
119
+ return this.buffer;
120
+ }
98
121
  }
99
122
  exports.Base64Image = Base64Image;
100
123
  Base64Image.strPrefix = 'data:image/png;base64,';
@@ -16,7 +16,22 @@ export declare class HttpClientGot {
16
16
  http: http.Agent;
17
17
  https: https.Agent;
18
18
  } | undefined);
19
+ /**
20
+ * Configures got with retry behavior for transient server errors.
21
+ *
22
+ * Got retries requests that fail with retryable status codes
23
+ * (500, 502, 503, 504, etc.) up to `limit` times using exponential backoff.
24
+ *
25
+ * POST requests are only retried if their URL is registered in `urlsToRetry`
26
+ * (see `shouldRetryOnError`), to avoid retrying non-idempotent calls
27
+ * to unknown endpoints.
28
+ */
19
29
  private buildGotExtendOptions;
30
+ /**
31
+ * Only retry POST requests if the URL is explicitly registered in `urlsToRetry`
32
+ * (populated by InferenceClient with inference endpoint URLs).
33
+ * Non-POST requests (GET, PUT, etc.) are always retried.
34
+ */
20
35
  private shouldRetryOnError;
21
36
  private shouldRetryPostRequest;
22
37
  private initHeaders;