askui 0.29.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 (84) 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/models/anthropic/askui-agent.d.ts +2 -2
  23. package/dist/cjs/core/models/anthropic/askui-agent.js +33 -33
  24. package/dist/cjs/core/models/anthropic/tools/os-agent-tools.d.ts +47 -3
  25. package/dist/cjs/core/models/anthropic/tools/os-agent-tools.js +220 -23
  26. package/dist/cjs/core/ui-control-commands/action.d.ts +1 -0
  27. package/dist/cjs/core/ui-control-commands/action.js +10 -2
  28. package/dist/cjs/core/ui-control-commands/control-command.d.ts +1 -0
  29. package/dist/cjs/core/ui-control-commands/control-command.js +7 -0
  30. package/dist/cjs/execution/dsl.d.ts +12 -5
  31. package/dist/cjs/execution/dsl.js +30 -15
  32. package/dist/cjs/execution/execution-runtime.d.ts +1 -1
  33. package/dist/cjs/execution/execution-runtime.js +21 -17
  34. package/dist/cjs/execution/inference-client.d.ts +5 -3
  35. package/dist/cjs/execution/inference-client.js +22 -3
  36. package/dist/cjs/execution/ui-control-client-dependency-builder.js +6 -1
  37. package/dist/cjs/execution/ui-control-client.d.ts +2 -2
  38. package/dist/cjs/execution/ui-control-client.js +29 -12
  39. package/dist/cjs/execution/ui-controller-client-interface.d.ts +2 -0
  40. package/dist/cjs/utils/base_64_image/base-64-image.d.ts +2 -0
  41. package/dist/cjs/utils/base_64_image/base-64-image.js +27 -4
  42. package/dist/esm/core/cache/cache-config.d.ts +5 -0
  43. package/dist/esm/core/cache/cache-config.js +1 -0
  44. package/dist/esm/core/cache/cache-entry-reference.d.ts +7 -0
  45. package/dist/esm/core/cache/cache-entry-reference.js +16 -0
  46. package/dist/esm/core/cache/cache-entry.d.ts +11 -0
  47. package/dist/esm/core/cache/cache-entry.js +43 -0
  48. package/dist/esm/core/cache/cache-interface.d.ts +11 -0
  49. package/dist/esm/core/cache/cache-interface.js +1 -0
  50. package/dist/esm/core/cache/cache-manager.d.ts +24 -0
  51. package/dist/esm/core/cache/cache-manager.js +141 -0
  52. package/dist/esm/core/cache/cahe-file.d.ts +19 -0
  53. package/dist/esm/core/cache/cahe-file.js +121 -0
  54. package/dist/esm/core/cache/dummy-cache-manager.d.ts +12 -0
  55. package/dist/esm/core/cache/dummy-cache-manager.js +23 -0
  56. package/dist/esm/core/cache/image-reference.d.ts +10 -0
  57. package/dist/esm/core/cache/image-reference.js +36 -0
  58. package/dist/esm/core/cache/index.d.ts +7 -0
  59. package/dist/esm/core/cache/index.js +5 -0
  60. package/dist/esm/core/model/annotation-result/boundary-box.js +1 -1
  61. package/dist/esm/core/model/custom-element.d.ts +1 -0
  62. package/dist/esm/core/model/custom-element.js +3 -0
  63. package/dist/esm/core/models/anthropic/askui-agent.d.ts +2 -2
  64. package/dist/esm/core/models/anthropic/askui-agent.js +34 -34
  65. package/dist/esm/core/models/anthropic/tools/os-agent-tools.d.ts +47 -3
  66. package/dist/esm/core/models/anthropic/tools/os-agent-tools.js +215 -22
  67. package/dist/esm/core/ui-control-commands/action.d.ts +1 -0
  68. package/dist/esm/core/ui-control-commands/action.js +10 -2
  69. package/dist/esm/core/ui-control-commands/control-command.d.ts +1 -0
  70. package/dist/esm/core/ui-control-commands/control-command.js +7 -0
  71. package/dist/esm/execution/dsl.d.ts +12 -5
  72. package/dist/esm/execution/dsl.js +30 -15
  73. package/dist/esm/execution/execution-runtime.d.ts +1 -1
  74. package/dist/esm/execution/execution-runtime.js +21 -17
  75. package/dist/esm/execution/inference-client.d.ts +5 -3
  76. package/dist/esm/execution/inference-client.js +22 -3
  77. package/dist/esm/execution/ui-control-client-dependency-builder.js +6 -1
  78. package/dist/esm/execution/ui-control-client.d.ts +2 -2
  79. package/dist/esm/execution/ui-control-client.js +29 -12
  80. package/dist/esm/execution/ui-controller-client-interface.d.ts +2 -0
  81. package/dist/esm/utils/base_64_image/base-64-image.d.ts +2 -0
  82. package/dist/esm/utils/base_64_image/base-64-image.js +27 -4
  83. package/package.json +1 -1
  84. package/dist/example_projects_templates/templates/askui-helper-windows.nj +0 -32
@@ -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
@@ -563,7 +564,6 @@ export declare class UiControlClient extends ApiCommands {
563
564
  * #### Cross-Platform Coordination
564
565
  * ```typescript
565
566
  * // Share context between desktop and mobile agents
566
- * await auiAndroid.agent.configureAsAndroidAgent();
567
567
  *
568
568
  * const history = await auiDesktop.act("Copy username from desktop app");
569
569
  * await auiAndroid.act("Paste username into mobile login", {
@@ -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 {
@@ -46,7 +47,7 @@ class UiControlClient extends dsl_1.ApiCommands {
46
47
  return __awaiter(this, void 0, void 0, function* () {
47
48
  const connectionState = yield this.executionRuntime.connect();
48
49
  yield this.agent.initializeOsAgentHandler();
49
- yield this.agent.configureAsDesktopAgent();
50
+ yield this.agent.configureAgent();
50
51
  return connectionState;
51
52
  });
52
53
  }
@@ -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,';
@@ -0,0 +1,5 @@
1
+ export type ValidationType = 'PixelPerfect';
2
+ export interface CacheConfig {
3
+ cacheFilePath?: string;
4
+ validationType?: ValidationType;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { ImageReference } from './image-reference';
2
+ export declare class CacheEntryReference {
3
+ image?: ImageReference | undefined;
4
+ constructor(image?: ImageReference | undefined);
5
+ static fromJson(json: CacheEntryReference): CacheEntryReference;
6
+ toJson(): Record<string, unknown>;
7
+ }
@@ -0,0 +1,16 @@
1
+ import { ImageReference } from './image-reference';
2
+ export class CacheEntryReference {
3
+ constructor(image) {
4
+ this.image = image;
5
+ }
6
+ static fromJson(json) {
7
+ return new CacheEntryReference(json.image ? ImageReference.fromJson(json.image) : undefined);
8
+ }
9
+ toJson() {
10
+ const jsonObject = {};
11
+ if (this.image !== undefined) {
12
+ jsonObject['image'] = this.image.toJson();
13
+ }
14
+ return jsonObject;
15
+ }
16
+ }
@@ -0,0 +1,11 @@
1
+ import { ControlCommand } from '../ui-control-commands';
2
+ import { CacheEntryReference } from './cache-entry-reference';
3
+ export declare class CacheEntry {
4
+ alwaysValid: boolean;
5
+ controlCommand: ControlCommand;
6
+ reference?: CacheEntryReference | undefined;
7
+ createdAt: Date;
8
+ constructor(alwaysValid: boolean, controlCommand: ControlCommand, reference?: CacheEntryReference | undefined, createdAt?: Date);
9
+ static fromJson(json: CacheEntry): CacheEntry | undefined;
10
+ toJson(): Record<string, unknown>;
11
+ }
@@ -0,0 +1,43 @@
1
+ import { ControlCommand } from '../ui-control-commands';
2
+ import { CacheEntryReference } from './cache-entry-reference';
3
+ import { logger } from '../../lib';
4
+ export class CacheEntry {
5
+ constructor(alwaysValid, controlCommand, reference, createdAt = new Date()) {
6
+ this.alwaysValid = alwaysValid;
7
+ this.controlCommand = controlCommand;
8
+ this.reference = reference;
9
+ this.createdAt = createdAt;
10
+ }
11
+ static fromJson(json) {
12
+ try {
13
+ if (json === undefined) {
14
+ throw new Error('Cache entry is undefined');
15
+ }
16
+ if (json.controlCommand === undefined) {
17
+ throw new Error('the key "controlCommand" is required');
18
+ }
19
+ if (json.createdAt === undefined) {
20
+ throw new Error('the key "createdAt" is required');
21
+ }
22
+ if (json.alwaysValid === undefined) {
23
+ throw new Error('the key "alwaysValid" is required');
24
+ }
25
+ return new CacheEntry(json.alwaysValid, ControlCommand.fromJson(json.controlCommand), json.reference ? CacheEntryReference.fromJson(json.reference) : undefined, new Date(json.createdAt));
26
+ }
27
+ catch (error) {
28
+ logger.error(`Error deserializing cache entry: ${error}`);
29
+ return undefined;
30
+ }
31
+ }
32
+ toJson() {
33
+ const jsonObject = {
34
+ alwaysValid: this.alwaysValid,
35
+ controlCommand: this.controlCommand.toJson(),
36
+ createdAt: this.createdAt,
37
+ };
38
+ if (this.reference !== undefined) {
39
+ jsonObject['reference'] = this.reference.toJson();
40
+ }
41
+ return jsonObject;
42
+ }
43
+ }
@@ -0,0 +1,11 @@
1
+ import { CustomElement } from '../model/custom-element';
2
+ import { ControlCommand } from '../ui-control-commands';
3
+ import { CacheEntry } from './cache-entry';
4
+ export interface CacheInterface {
5
+ loadFromFile(): void;
6
+ saveToFile(): void;
7
+ add(key: string, value: CacheEntry): void;
8
+ addCacheEntryFromControlCommand(instruction: string, controlCommand: ControlCommand, customElements: CustomElement[], image?: string): Promise<void>;
9
+ isImageRequired(instruction: string, customElements: CustomElement[]): boolean | undefined;
10
+ getCachedControlCommand(instruction: string, customElements: CustomElement[], image?: string): Promise<ControlCommand | undefined>;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ import { ControlCommand } from '../ui-control-commands';
2
+ import { CacheInterface } from './cache-interface';
3
+ import { CacheEntry } from './cache-entry';
4
+ import { CacheConfig, ValidationType } from './cache-config';
5
+ import { CustomElement } from '../model/custom-element';
6
+ export declare class CacheManager implements CacheInterface {
7
+ private relevantData;
8
+ validationType: ValidationType;
9
+ private cacheFile;
10
+ private readonly referenceWidth;
11
+ private readonly referenceHeight;
12
+ private readonly defaultValidationType;
13
+ constructor(config: CacheConfig);
14
+ loadFromFile(): void;
15
+ saveToFile(): void;
16
+ add(key: string, value: CacheEntry): void;
17
+ addCacheEntryFromControlCommand(instruction: string, controlCommand: ControlCommand, customElements: CustomElement[], image?: string): Promise<void>;
18
+ isImageRequired(instruction: string, customElements: CustomElement[]): boolean | undefined;
19
+ getCachedControlCommand(instruction: string, customElements: CustomElement[], image?: string): Promise<ControlCommand | undefined>;
20
+ private encodeKey;
21
+ private getCacheEntriesByKey;
22
+ private getValidCacheEntry;
23
+ private validateAccordingToPixelPerfect;
24
+ }