@browserbasehq/stagehand 1.0.3 → 1.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +10 -5
  2. package/dist/evals/index.eval.js +1075 -0
  3. package/dist/evals/index.eval.js.map +1 -0
  4. package/dist/evals/playground.js +112 -0
  5. package/dist/evals/playground.js.map +1 -0
  6. package/dist/evals/utils.js +52 -0
  7. package/dist/evals/utils.js.map +1 -0
  8. package/dist/examples/2048.js +108 -0
  9. package/dist/examples/2048.js.map +1 -0
  10. package/dist/examples/debugUrl.js +35 -0
  11. package/dist/examples/debugUrl.js.map +1 -0
  12. package/dist/examples/example.js +37 -0
  13. package/dist/examples/example.js.map +1 -0
  14. package/dist/index.d.ts +22 -6
  15. package/dist/index.js +629 -152
  16. package/dist/lib/browserbase.js +56 -0
  17. package/dist/lib/browserbase.js.map +1 -0
  18. package/dist/lib/cache.js +78 -0
  19. package/dist/lib/cache.js.map +1 -0
  20. package/dist/lib/dom/debug.js +119 -0
  21. package/dist/lib/dom/debug.js.map +1 -0
  22. package/dist/lib/dom/index.js +20 -0
  23. package/dist/lib/dom/index.js.map +1 -0
  24. package/dist/lib/dom/process.js +396 -0
  25. package/dist/lib/dom/process.js.map +1 -0
  26. package/dist/lib/dom/utils.js +28 -0
  27. package/dist/lib/dom/utils.js.map +1 -0
  28. package/dist/lib/index.js +978 -0
  29. package/dist/lib/index.js.map +1 -0
  30. package/dist/lib/inference.js +226 -0
  31. package/dist/lib/inference.js.map +1 -0
  32. package/dist/lib/llm/AnthropicClient.js +150 -0
  33. package/dist/lib/llm/AnthropicClient.js.map +1 -0
  34. package/dist/lib/llm/LLMClient.js +12 -0
  35. package/dist/lib/llm/LLMClient.js.map +1 -0
  36. package/dist/lib/llm/LLMProvider.js +34 -0
  37. package/dist/lib/llm/LLMProvider.js.map +1 -0
  38. package/dist/lib/llm/OpenAIClient.js +69 -0
  39. package/dist/lib/llm/OpenAIClient.js.map +1 -0
  40. package/dist/lib/prompt.js +288 -0
  41. package/dist/lib/prompt.js.map +1 -0
  42. package/dist/lib/vision.js +194 -0
  43. package/dist/lib/vision.js.map +1 -0
  44. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -84,6 +84,7 @@ module.exports = __toCommonJS(lib_exports);
84
84
  var import_test = require("@playwright/test");
85
85
  var import_crypto = __toESM(require("crypto"));
86
86
  var import_fs2 = __toESM(require("fs"));
87
+ var import_sdk2 = require("@browserbasehq/sdk");
87
88
 
88
89
  // lib/prompt.ts
89
90
  var actSystemPrompt = `
@@ -334,6 +335,7 @@ var modelsWithVision = [
334
335
  "gpt-4o-mini",
335
336
  "claude-3-5-sonnet-latest",
336
337
  "claude-3-5-sonnet-20240620",
338
+ "claude-3-5-sonnet-20241022",
337
339
  "gpt-4o-2024-08-06"
338
340
  ];
339
341
  var AnnotatedScreenshotText = "This is a screenshot of the current page state with the elements annotated on it. Each element id is annotated with a number to the top left of it. Duplicate annotations at the same location are under each other vertically.";
@@ -347,9 +349,10 @@ function verifyActCompletion(_0) {
347
349
  modelName,
348
350
  screenshot,
349
351
  domElements,
350
- logger
352
+ logger,
353
+ requestId
351
354
  }) {
352
- const llmClient = llmProvider.getClient(modelName);
355
+ const llmClient = llmProvider.getClient(modelName, requestId);
353
356
  const messages = [
354
357
  buildVerifyActCompletionSystemPrompt(),
355
358
  buildVerifyActCompletionUserPrompt(goal, steps, domElements)
@@ -398,9 +401,10 @@ function act(_0) {
398
401
  modelName,
399
402
  screenshot,
400
403
  retries = 0,
401
- logger
404
+ logger,
405
+ requestId
402
406
  }) {
403
- const llmClient = llmProvider.getClient(modelName);
407
+ const llmClient = llmProvider.getClient(modelName, requestId);
404
408
  const messages = [
405
409
  buildActSystemPrompt(),
406
410
  buildActUserPrompt(action, steps, domElements)
@@ -437,7 +441,8 @@ function act(_0) {
437
441
  llmProvider,
438
442
  modelName,
439
443
  retries: retries + 1,
440
- logger
444
+ logger,
445
+ requestId
441
446
  });
442
447
  }
443
448
  });
@@ -452,9 +457,10 @@ function extract(_0) {
452
457
  llmProvider,
453
458
  modelName,
454
459
  chunksSeen,
455
- chunksTotal
460
+ chunksTotal,
461
+ requestId
456
462
  }) {
457
- const llmClient = llmProvider.getClient(modelName);
463
+ const llmClient = llmProvider.getClient(modelName, requestId);
458
464
  const extractionResponse = yield llmClient.createChatCompletion({
459
465
  model: modelName,
460
466
  messages: [
@@ -527,7 +533,8 @@ function observe(_0) {
527
533
  domElements,
528
534
  llmProvider,
529
535
  modelName,
530
- image
536
+ image,
537
+ requestId
531
538
  }) {
532
539
  const observeSchema = import_zod.z.object({
533
540
  elements: import_zod.z.array(
@@ -539,7 +546,7 @@ function observe(_0) {
539
546
  })
540
547
  ).describe("an array of elements that match the instruction")
541
548
  });
542
- const llmClient = llmProvider.getClient(modelName);
549
+ const llmClient = llmProvider.getClient(modelName, requestId);
543
550
  const observationResponse = yield llmClient.createChatCompletion({
544
551
  model: modelName,
545
552
  messages: [
@@ -567,12 +574,31 @@ function observe(_0) {
567
574
  var import_openai = __toESM(require("openai"));
568
575
  var import_zod2 = require("openai/helpers/zod");
569
576
  var OpenAIClient = class {
570
- constructor(logger) {
577
+ constructor(logger, enableCaching = false, cache, requestId) {
571
578
  this.client = new import_openai.default();
572
579
  this.logger = logger;
580
+ this.requestId = requestId;
581
+ this.cache = cache;
582
+ this.enableCaching = enableCaching;
573
583
  }
574
584
  createChatCompletion(options) {
575
585
  return __async(this, null, function* () {
586
+ const cacheOptions = {
587
+ model: options.model,
588
+ messages: options.messages,
589
+ temperature: options.temperature,
590
+ top_p: options.top_p,
591
+ frequency_penalty: options.frequency_penalty,
592
+ presence_penalty: options.presence_penalty,
593
+ image: options.image,
594
+ response_model: options.response_model
595
+ };
596
+ if (this.enableCaching) {
597
+ const cachedResponse = yield this.cache.get(cacheOptions, this.requestId);
598
+ if (cachedResponse) {
599
+ return cachedResponse;
600
+ }
601
+ }
576
602
  if (options.image) {
577
603
  const screenshotMessage = {
578
604
  role: "user",
@@ -602,8 +628,18 @@ var OpenAIClient = class {
602
628
  if (response_model) {
603
629
  const extractedData = response.choices[0].message.content;
604
630
  const parsedData = JSON.parse(extractedData);
631
+ if (this.enableCaching) {
632
+ this.cache.set(
633
+ cacheOptions,
634
+ __spreadValues({}, parsedData),
635
+ this.requestId
636
+ );
637
+ }
605
638
  return __spreadValues({}, parsedData);
606
639
  }
640
+ if (this.enableCaching) {
641
+ this.cache.set(cacheOptions, response, this.requestId);
642
+ }
607
643
  return response;
608
644
  });
609
645
  }
@@ -613,16 +649,33 @@ var OpenAIClient = class {
613
649
  var import_sdk = __toESM(require("@anthropic-ai/sdk"));
614
650
  var import_zod_to_json_schema = require("zod-to-json-schema");
615
651
  var AnthropicClient = class {
616
- constructor(logger) {
652
+ constructor(logger, enableCaching = false, cache, requestId) {
617
653
  this.client = new import_sdk.default({
618
654
  apiKey: process.env.ANTHROPIC_API_KEY
619
- // Make sure to set this environment variable
620
655
  });
621
656
  this.logger = logger;
657
+ this.cache = cache;
658
+ this.enableCaching = enableCaching;
659
+ this.requestId = requestId;
622
660
  }
623
661
  createChatCompletion(options) {
624
662
  return __async(this, null, function* () {
625
663
  var _a, _b, _c, _d, _e, _f, _g;
664
+ const cacheOptions = {
665
+ model: options.model,
666
+ messages: options.messages,
667
+ temperature: options.temperature,
668
+ image: options.image,
669
+ response_model: options.response_model,
670
+ tools: options.tools,
671
+ retries: options.retries
672
+ };
673
+ if (this.enableCaching) {
674
+ const cachedResponse = yield this.cache.get(cacheOptions, this.requestId);
675
+ if (cachedResponse) {
676
+ return cachedResponse;
677
+ }
678
+ }
626
679
  const systemMessage = options.messages.find((msg) => msg.role === "system");
627
680
  const userMessages = options.messages.filter(
628
681
  (msg) => msg.role !== "system"
@@ -724,26 +777,318 @@ var AnthropicClient = class {
724
777
  if (options.response_model) {
725
778
  const toolUse = response.content.find((c) => c.type === "tool_use");
726
779
  if (toolUse && "input" in toolUse) {
727
- return toolUse.input;
780
+ const result = toolUse.input;
781
+ if (this.enableCaching) {
782
+ this.cache.set(cacheOptions, result, this.requestId);
783
+ }
784
+ return result;
728
785
  } else {
729
- if (!options.retries || options.retries < 2) {
786
+ if (!options.retries || options.retries < 5) {
730
787
  return this.createChatCompletion(__spreadProps(__spreadValues({}, options), {
731
788
  retries: ((_g = options.retries) != null ? _g : 0) + 1
732
789
  }));
733
790
  }
734
791
  throw new Error(
735
- "Extraction failed: No tool use with input in response"
792
+ "Create Chat Completion Failed: No tool use with input in response"
736
793
  );
737
794
  }
738
795
  }
796
+ if (this.enableCaching) {
797
+ this.cache.set(cacheOptions, transformedResponse, this.requestId);
798
+ }
739
799
  return transformedResponse;
740
800
  });
741
801
  }
742
802
  };
743
803
 
804
+ // lib/llm/LLMCache.ts
805
+ var fs = __toESM(require("fs"));
806
+ var path = __toESM(require("path"));
807
+ var crypto = __toESM(require("crypto"));
808
+ var LLMCache = class {
809
+ constructor(logger, cacheDir = path.join(process.cwd(), "tmp", ".cache"), cacheFile = "llm_calls.json") {
810
+ this.CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
811
+ // 1 week in milliseconds
812
+ this.CLEANUP_PROBABILITY = 0.01;
813
+ // 1% chance
814
+ this.LOCK_TIMEOUT_MS = 1e3;
815
+ this.lock_acquired = false;
816
+ this.count_lock_acquire_failures = 0;
817
+ this.request_id_to_used_hashes = {};
818
+ this.logger = logger;
819
+ this.cacheDir = cacheDir;
820
+ this.cacheFile = path.join(cacheDir, cacheFile);
821
+ this.lockFile = path.join(cacheDir, "llm_cache.lock");
822
+ this.ensureCacheDirectory();
823
+ this.setupProcessHandlers();
824
+ }
825
+ setupProcessHandlers() {
826
+ const releaseLockAndExit = () => {
827
+ this.releaseLock();
828
+ process.exit();
829
+ };
830
+ process.on("exit", releaseLockAndExit);
831
+ process.on("SIGINT", releaseLockAndExit);
832
+ process.on("SIGTERM", releaseLockAndExit);
833
+ process.on("uncaughtException", (err) => {
834
+ this.logger({
835
+ category: "llm_cache",
836
+ message: `Uncaught exception: ${err}`,
837
+ level: 2
838
+ });
839
+ if (this.lock_acquired) {
840
+ releaseLockAndExit();
841
+ }
842
+ });
843
+ }
844
+ ensureCacheDirectory() {
845
+ if (!fs.existsSync(this.cacheDir)) {
846
+ fs.mkdirSync(this.cacheDir, { recursive: true });
847
+ }
848
+ }
849
+ createHash(data) {
850
+ const hash = crypto.createHash("sha256");
851
+ return hash.update(JSON.stringify(data)).digest("hex");
852
+ }
853
+ sleep(ms) {
854
+ return new Promise((resolve) => setTimeout(resolve, ms));
855
+ }
856
+ acquireLock() {
857
+ return __async(this, null, function* () {
858
+ const startTime = Date.now();
859
+ while (Date.now() - startTime < this.LOCK_TIMEOUT_MS) {
860
+ try {
861
+ if (fs.existsSync(this.lockFile)) {
862
+ const lockAge = Date.now() - fs.statSync(this.lockFile).mtimeMs;
863
+ if (lockAge > this.LOCK_TIMEOUT_MS) {
864
+ fs.unlinkSync(this.lockFile);
865
+ }
866
+ }
867
+ fs.writeFileSync(this.lockFile, process.pid.toString(), { flag: "wx" });
868
+ this.count_lock_acquire_failures = 0;
869
+ this.lock_acquired = true;
870
+ return true;
871
+ } catch (error) {
872
+ yield this.sleep(5);
873
+ }
874
+ }
875
+ this.logger({
876
+ category: "llm_cache",
877
+ message: "Failed to acquire lock after timeout",
878
+ level: 2
879
+ });
880
+ this.count_lock_acquire_failures++;
881
+ if (this.count_lock_acquire_failures >= 3) {
882
+ this.logger({
883
+ category: "llm_cache",
884
+ message: "Failed to acquire lock 3 times in a row. Releasing lock manually.",
885
+ level: 1
886
+ });
887
+ this.releaseLock();
888
+ }
889
+ return false;
890
+ });
891
+ }
892
+ releaseLock() {
893
+ try {
894
+ if (fs.existsSync(this.lockFile)) {
895
+ fs.unlinkSync(this.lockFile);
896
+ }
897
+ this.lock_acquired = false;
898
+ } catch (error) {
899
+ this.logger({
900
+ category: "llm_cache",
901
+ message: `Error releasing lock: ${error}`,
902
+ level: 2
903
+ });
904
+ }
905
+ }
906
+ readCache() {
907
+ if (fs.existsSync(this.cacheFile)) {
908
+ return JSON.parse(fs.readFileSync(this.cacheFile, "utf-8"));
909
+ }
910
+ return {};
911
+ }
912
+ writeCache(cache) {
913
+ try {
914
+ if (Math.random() < this.CLEANUP_PROBABILITY) {
915
+ this.cleanupStaleEntries(cache);
916
+ }
917
+ fs.writeFileSync(this.cacheFile, JSON.stringify(cache, null, 2));
918
+ } finally {
919
+ this.releaseLock();
920
+ }
921
+ }
922
+ cleanupStaleEntries(cache) {
923
+ if (!this.acquireLock()) {
924
+ this.logger({
925
+ category: "llm_cache",
926
+ message: "Failed to acquire lock for cleaning up cache",
927
+ level: 2
928
+ });
929
+ return;
930
+ }
931
+ try {
932
+ const now = Date.now();
933
+ let entriesRemoved = 0;
934
+ for (const [hash, entry] of Object.entries(cache)) {
935
+ if (now - entry.timestamp > this.CACHE_MAX_AGE_MS) {
936
+ delete cache[hash];
937
+ entriesRemoved++;
938
+ }
939
+ }
940
+ if (entriesRemoved > 0) {
941
+ this.logger({
942
+ category: "llm_cache",
943
+ message: `Cleaned up ${entriesRemoved} stale cache entries`,
944
+ level: 1
945
+ });
946
+ }
947
+ } catch (error) {
948
+ this.logger({
949
+ category: "llm_cache",
950
+ message: `Error cleaning up stale cache entries: ${error}`,
951
+ level: 1
952
+ });
953
+ } finally {
954
+ this.releaseLock();
955
+ }
956
+ }
957
+ resetCache() {
958
+ if (!this.acquireLock()) {
959
+ this.logger({
960
+ category: "llm_cache",
961
+ message: "Failed to acquire lock for resetting cache",
962
+ level: 2
963
+ });
964
+ return;
965
+ }
966
+ try {
967
+ this.ensureCacheDirectory();
968
+ fs.writeFileSync(this.cacheFile, "{}");
969
+ } finally {
970
+ this.releaseLock();
971
+ }
972
+ }
973
+ get(options, requestId) {
974
+ return __async(this, null, function* () {
975
+ var _a, _b;
976
+ if (!(yield this.acquireLock())) {
977
+ this.logger({
978
+ category: "llm_cache",
979
+ message: "Failed to acquire lock for getting cache",
980
+ level: 2
981
+ });
982
+ return null;
983
+ }
984
+ try {
985
+ const hash = this.createHash(options);
986
+ const cache = this.readCache();
987
+ if (cache[hash]) {
988
+ this.logger({
989
+ category: "llm_cache",
990
+ message: "Cache hit",
991
+ level: 1
992
+ });
993
+ (_b = (_a = this.request_id_to_used_hashes)[requestId]) != null ? _b : _a[requestId] = [];
994
+ this.request_id_to_used_hashes[requestId].push(hash);
995
+ return cache[hash].response;
996
+ }
997
+ return null;
998
+ } catch (error) {
999
+ this.logger({
1000
+ category: "llm_cache",
1001
+ message: `Error getting cache: ${error}. Resetting cache.`,
1002
+ level: 1
1003
+ });
1004
+ this.resetCache();
1005
+ return null;
1006
+ } finally {
1007
+ this.releaseLock();
1008
+ }
1009
+ });
1010
+ }
1011
+ deleteCacheForRequestId(requestId) {
1012
+ return __async(this, null, function* () {
1013
+ var _a;
1014
+ if (!(yield this.acquireLock())) {
1015
+ this.logger({
1016
+ category: "llm_cache",
1017
+ message: "Failed to acquire lock for deleting cache",
1018
+ level: 2
1019
+ });
1020
+ return;
1021
+ }
1022
+ try {
1023
+ const cache = this.readCache();
1024
+ let entriesRemoved = [];
1025
+ for (const hash of (_a = this.request_id_to_used_hashes[requestId]) != null ? _a : []) {
1026
+ if (cache[hash]) {
1027
+ entriesRemoved.push(cache[hash]);
1028
+ delete cache[hash];
1029
+ }
1030
+ }
1031
+ this.logger({
1032
+ category: "llm_cache",
1033
+ message: `Deleted ${entriesRemoved.length} cache entries for requestId ${requestId}`,
1034
+ level: 1
1035
+ });
1036
+ this.writeCache(cache);
1037
+ } catch (exception) {
1038
+ this.logger({
1039
+ category: "llm_cache",
1040
+ message: `Error deleting cache for requestId ${requestId}: ${exception}`,
1041
+ level: 1
1042
+ });
1043
+ } finally {
1044
+ this.releaseLock();
1045
+ }
1046
+ });
1047
+ }
1048
+ set(options, response, requestId) {
1049
+ return __async(this, null, function* () {
1050
+ var _a, _b;
1051
+ if (!(yield this.acquireLock())) {
1052
+ this.logger({
1053
+ category: "llm_cache",
1054
+ message: "Failed to acquire lock for setting cache",
1055
+ level: 2
1056
+ });
1057
+ return;
1058
+ }
1059
+ try {
1060
+ const hash = this.createHash(options);
1061
+ const cache = this.readCache();
1062
+ cache[hash] = {
1063
+ response,
1064
+ timestamp: Date.now(),
1065
+ requestId
1066
+ };
1067
+ this.writeCache(cache);
1068
+ (_b = (_a = this.request_id_to_used_hashes)[requestId]) != null ? _b : _a[requestId] = [];
1069
+ this.request_id_to_used_hashes[requestId].push(hash);
1070
+ this.logger({
1071
+ category: "llm_cache",
1072
+ message: "Cache miss - saved new response",
1073
+ level: 1
1074
+ });
1075
+ } catch (error) {
1076
+ this.logger({
1077
+ category: "llm_cache",
1078
+ message: `Error setting cache: ${error}. Resetting cache.`,
1079
+ level: 1
1080
+ });
1081
+ this.resetCache();
1082
+ } finally {
1083
+ this.releaseLock();
1084
+ }
1085
+ });
1086
+ }
1087
+ };
1088
+
744
1089
  // lib/llm/LLMProvider.ts
745
1090
  var LLMProvider = class {
746
- constructor(logger) {
1091
+ constructor(logger, enableCaching) {
747
1092
  this.modelToProviderMap = {
748
1093
  "gpt-4o": "openai",
749
1094
  "gpt-4o-mini": "openai",
@@ -753,17 +1098,36 @@ var LLMProvider = class {
753
1098
  "claude-3-5-sonnet-20241022": "anthropic"
754
1099
  };
755
1100
  this.logger = logger;
1101
+ this.enableCaching = enableCaching;
1102
+ this.cache = new LLMCache(logger);
756
1103
  }
757
- getClient(modelName) {
1104
+ cleanRequestCache(requestId) {
1105
+ this.logger({
1106
+ category: "llm_cache",
1107
+ message: `Cleaning up cache for requestId: ${requestId}`
1108
+ });
1109
+ this.cache.deleteCacheForRequestId(requestId);
1110
+ }
1111
+ getClient(modelName, requestId) {
758
1112
  const provider = this.modelToProviderMap[modelName];
759
1113
  if (!provider) {
760
1114
  throw new Error(`Unsupported model: ${modelName}`);
761
1115
  }
762
1116
  switch (provider) {
763
1117
  case "openai":
764
- return new OpenAIClient(this.logger);
1118
+ return new OpenAIClient(
1119
+ this.logger,
1120
+ this.enableCaching,
1121
+ this.cache,
1122
+ requestId
1123
+ );
765
1124
  case "anthropic":
766
- return new AnthropicClient(this.logger);
1125
+ return new AnthropicClient(
1126
+ this.logger,
1127
+ this.enableCaching,
1128
+ this.cache,
1129
+ requestId
1130
+ );
767
1131
  default:
768
1132
  throw new Error(`Unsupported provider: ${provider}`);
769
1133
  }
@@ -773,55 +1137,6 @@ var LLMProvider = class {
773
1137
  // lib/index.ts
774
1138
  var import_path2 = __toESM(require("path"));
775
1139
 
776
- // lib/browserbase.ts
777
- var Browserbase = class {
778
- createSession() {
779
- return __async(this, null, function* () {
780
- if (!process.env.BROWSERBASE_API_KEY || !process.env.BROWSERBASE_PROJECT_ID) {
781
- throw new Error(
782
- "BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID must be set"
783
- );
784
- }
785
- const response = yield fetch(`https://www.browserbase.com/v1/sessions`, {
786
- method: "POST",
787
- headers: {
788
- "x-bb-api-key": `${process.env.BROWSERBASE_API_KEY}`,
789
- "Content-Type": "application/json"
790
- },
791
- body: JSON.stringify({
792
- projectId: process.env.BROWSERBASE_PROJECT_ID
793
- })
794
- });
795
- const json = yield response.json();
796
- if (json.error) {
797
- throw new Error(json.error);
798
- }
799
- return {
800
- sessionId: json.id,
801
- connectUrl: json.connectUrl
802
- };
803
- });
804
- }
805
- retrieveDebugConnectionURL(sessionId) {
806
- return __async(this, null, function* () {
807
- if (!process.env.BROWSERBASE_API_KEY) {
808
- throw new Error("BROWSERBASE_API_KEY must be set");
809
- }
810
- const response = yield fetch(
811
- `https://www.browserbase.com/v1/sessions/${sessionId}/debug`,
812
- {
813
- method: "GET",
814
- headers: {
815
- "x-bb-api-key": `${process.env.BROWSERBASE_API_KEY}`
816
- }
817
- }
818
- );
819
- const json = yield response.json();
820
- return json.debuggerFullscreenUrl;
821
- });
822
- }
823
- };
824
-
825
1140
  // lib/vision.ts
826
1141
  var import_fs = __toESM(require("fs"));
827
1142
  var import_path = __toESM(require("path"));
@@ -1004,40 +1319,85 @@ var ScreenshotService = class _ScreenshotService {
1004
1319
 
1005
1320
  // lib/index.ts
1006
1321
  require("dotenv").config({ path: ".env" });
1007
- function getBrowser(env = "LOCAL", headless = false, logger) {
1322
+ function getBrowser(apiKey, projectId, env = "LOCAL", headless = false, logger, browserbaseSessionCreateParams, browserbaseResumeSessionID) {
1008
1323
  return __async(this, null, function* () {
1009
- if (env === "BROWSERBASE" && !process.env.BROWSERBASE_API_KEY) {
1010
- logger({
1011
- category: "Init",
1012
- message: "BROWSERBASE_API_KEY is required to use BROWSERBASE env. Defaulting to LOCAL.",
1013
- level: 0
1014
- });
1015
- env = "LOCAL";
1016
- }
1017
- if (env === "BROWSERBASE" && !process.env.BROWSERBASE_PROJECT_ID) {
1018
- logger({
1019
- category: "Init",
1020
- message: "BROWSERBASE_PROJECT_ID is required to use BROWSERBASE env. Defaulting to LOCAL.",
1021
- level: 0
1022
- });
1023
- env = "LOCAL";
1324
+ if (env === "BROWSERBASE") {
1325
+ if (!apiKey) {
1326
+ logger({
1327
+ category: "Init",
1328
+ message: "BROWSERBASE_API_KEY is required to use BROWSERBASE env. Defaulting to LOCAL.",
1329
+ level: 0
1330
+ });
1331
+ env = "LOCAL";
1332
+ }
1333
+ if (!projectId) {
1334
+ logger({
1335
+ category: "Init",
1336
+ message: "BROWSERBASE_PROJECT_ID is required for some Browserbase features that may not work without it.",
1337
+ level: 1
1338
+ });
1339
+ }
1024
1340
  }
1025
1341
  if (env === "BROWSERBASE") {
1342
+ if (!apiKey) {
1343
+ throw new Error("BROWSERBASE_API_KEY is required.");
1344
+ }
1026
1345
  let debugUrl = void 0;
1027
1346
  let sessionUrl = void 0;
1028
- logger({
1029
- category: "Init",
1030
- message: "Connecting you to Browserbase...",
1031
- level: 0
1347
+ let sessionId;
1348
+ let connectUrl;
1349
+ const browserbase = new import_sdk2.Browserbase({
1350
+ apiKey
1032
1351
  });
1033
- const browserbase = new Browserbase();
1034
- const { sessionId, connectUrl } = yield browserbase.createSession();
1352
+ if (browserbaseResumeSessionID) {
1353
+ try {
1354
+ const sessionStatus = yield browserbase.sessions.retrieve(
1355
+ browserbaseResumeSessionID
1356
+ );
1357
+ if (sessionStatus.status !== "RUNNING") {
1358
+ throw new Error(
1359
+ `Session ${browserbaseResumeSessionID} is not running (status: ${sessionStatus.status})`
1360
+ );
1361
+ }
1362
+ sessionId = browserbaseResumeSessionID;
1363
+ connectUrl = `wss://connect.browserbase.com?apiKey=${apiKey}&sessionId=${sessionId}`;
1364
+ logger({
1365
+ category: "Init",
1366
+ message: "Resuming existing Browserbase session...",
1367
+ level: 0
1368
+ });
1369
+ } catch (error) {
1370
+ logger({
1371
+ category: "Init",
1372
+ message: `Failed to resume session ${browserbaseResumeSessionID}: ${error.message}`,
1373
+ level: 0
1374
+ });
1375
+ throw error;
1376
+ }
1377
+ } else {
1378
+ logger({
1379
+ category: "Init",
1380
+ message: "Creating new Browserbase session...",
1381
+ level: 0
1382
+ });
1383
+ if (!projectId) {
1384
+ throw new Error(
1385
+ "BROWSERBASE_PROJECT_ID is required for new Browserbase sessions."
1386
+ );
1387
+ }
1388
+ const session = yield browserbase.sessions.create(__spreadValues({
1389
+ projectId
1390
+ }, browserbaseSessionCreateParams));
1391
+ sessionId = session.id;
1392
+ connectUrl = session.connectUrl;
1393
+ }
1035
1394
  const browser = yield import_test.chromium.connectOverCDP(connectUrl);
1036
- debugUrl = yield browserbase.retrieveDebugConnectionURL(sessionId);
1395
+ const { debuggerUrl } = yield browserbase.sessions.debug(sessionId);
1396
+ debugUrl = debuggerUrl;
1037
1397
  sessionUrl = `https://www.browserbase.com/sessions/${sessionId}`;
1038
1398
  logger({
1039
1399
  category: "Init",
1040
- message: `Browserbase session started.
1400
+ message: `Browserbase session ${browserbaseResumeSessionID ? "resumed" : "started"}.
1041
1401
 
1042
1402
  Session Url: ${sessionUrl}
1043
1403
 
@@ -1124,12 +1484,17 @@ function applyStealthScripts(context) {
1124
1484
  var Stagehand = class {
1125
1485
  constructor({
1126
1486
  env,
1127
- verbose = 0,
1128
- debugDom = false,
1487
+ apiKey,
1488
+ projectId,
1489
+ verbose,
1490
+ debugDom,
1129
1491
  llmProvider,
1130
- headless = false,
1492
+ headless,
1131
1493
  logger,
1132
- domSettleTimeoutMs = 6e4
1494
+ browserBaseSessionCreateParams,
1495
+ domSettleTimeoutMs,
1496
+ enableCaching,
1497
+ browserbaseResumeSessionID
1133
1498
  } = {
1134
1499
  env: "BROWSERBASE"
1135
1500
  }) {
@@ -1138,24 +1503,34 @@ var Stagehand = class {
1138
1503
  this.is_processing_browserbase_logs = false;
1139
1504
  this.externalLogger = logger;
1140
1505
  this.logger = this.log.bind(this);
1141
- this.llmProvider = llmProvider || new LLMProvider(this.logger);
1506
+ this.enableCaching = enableCaching != null ? enableCaching : false;
1507
+ this.llmProvider = llmProvider || new LLMProvider(this.logger, this.enableCaching);
1142
1508
  this.env = env;
1143
1509
  this.observations = {};
1510
+ this.apiKey = apiKey;
1511
+ this.projectId = projectId;
1144
1512
  this.actions = {};
1145
- this.verbose = verbose;
1146
- this.debugDom = debugDom;
1513
+ this.verbose = verbose != null ? verbose : 0;
1514
+ this.debugDom = debugDom != null ? debugDom : false;
1147
1515
  this.defaultModelName = "gpt-4o";
1148
- this.headless = headless;
1149
- this.domSettleTimeoutMs = domSettleTimeoutMs;
1516
+ this.domSettleTimeoutMs = domSettleTimeoutMs != null ? domSettleTimeoutMs : 3e4;
1517
+ this.headless = headless != null ? headless : false;
1518
+ this.browserBaseSessionCreateParams = browserBaseSessionCreateParams;
1519
+ this.browserbaseResumeSessionID = browserbaseResumeSessionID;
1150
1520
  }
1151
1521
  init() {
1152
1522
  return __async(this, arguments, function* ({
1153
1523
  modelName = "gpt-4o"
1154
1524
  } = {}) {
1525
+ console.log("haha fucker");
1155
1526
  const { context, debugUrl, sessionUrl } = yield getBrowser(
1527
+ this.apiKey,
1528
+ this.projectId,
1156
1529
  this.env,
1157
1530
  this.headless,
1158
- this.logger
1531
+ this.logger,
1532
+ this.browserBaseSessionCreateParams,
1533
+ this.browserbaseResumeSessionID
1159
1534
  ).catch((e) => {
1160
1535
  console.error("Error in init:", e);
1161
1536
  return { context: void 0, debugUrl: void 0, sessionUrl: void 0 };
@@ -1173,14 +1548,23 @@ var Stagehand = class {
1173
1548
  if (this.headless) {
1174
1549
  yield this.page.setViewportSize({ width: 1280, height: 720 });
1175
1550
  }
1176
- yield this.page.addInitScript({
1177
- path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "process.js")
1551
+ yield this.context.addInitScript({
1552
+ content: import_fs2.default.readFileSync(
1553
+ import_path2.default.join(__dirname, "..", "dist", "dom", "build", "process.js"),
1554
+ "utf8"
1555
+ )
1178
1556
  });
1179
- yield this.page.addInitScript({
1180
- path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "utils.js")
1557
+ yield this.context.addInitScript({
1558
+ content: import_fs2.default.readFileSync(
1559
+ import_path2.default.join(__dirname, "..", "dist", "dom", "build", "utils.js"),
1560
+ "utf8"
1561
+ )
1181
1562
  });
1182
- yield this.page.addInitScript({
1183
- path: import_path2.default.join(__dirname, "..", "dist", "dom", "build", "debug.js")
1563
+ yield this.context.addInitScript({
1564
+ content: import_fs2.default.readFileSync(
1565
+ import_path2.default.join(__dirname, "..", "dist", "dom", "build", "debug.js"),
1566
+ "utf8"
1567
+ )
1184
1568
  });
1185
1569
  return { debugUrl, sessionUrl };
1186
1570
  });
@@ -1271,11 +1655,9 @@ var Stagehand = class {
1271
1655
  return __async(this, null, function* () {
1272
1656
  try {
1273
1657
  const timeout = timeoutMs != null ? timeoutMs : this.domSettleTimeoutMs;
1274
- const timeoutPromise = new Promise((resolve) => {
1275
- setTimeout(() => {
1276
- console.warn(
1277
- `[stagehand:dom] DOM settle timeout of ${timeout}ms exceeded, continuing anyway`
1278
- );
1658
+ let timeoutHandle;
1659
+ const timeoutPromise = new Promise((resolve, reject) => {
1660
+ timeoutHandle = setTimeout(() => {
1279
1661
  this.log({
1280
1662
  category: "dom",
1281
1663
  message: `DOM settle timeout of ${timeout}ms exceeded, continuing anyway`,
@@ -1284,16 +1666,12 @@ var Stagehand = class {
1284
1666
  resolve();
1285
1667
  }, timeout);
1286
1668
  });
1287
- yield Promise.race([
1288
- (() => __async(this, null, function* () {
1289
- yield this.page.waitForSelector("body");
1290
- yield this.page.waitForLoadState("domcontentloaded");
1291
- yield this.page.evaluate(() => {
1669
+ try {
1670
+ yield Promise.race([
1671
+ this.page.evaluate(() => {
1292
1672
  return new Promise((resolve) => {
1293
1673
  if (typeof window.waitForDomSettle === "function") {
1294
- window.waitForDomSettle().then(() => {
1295
- resolve();
1296
- });
1674
+ window.waitForDomSettle().then(resolve);
1297
1675
  } else {
1298
1676
  console.warn(
1299
1677
  "waitForDomSettle is not defined, considering DOM as settled"
@@ -1301,10 +1679,14 @@ var Stagehand = class {
1301
1679
  resolve();
1302
1680
  }
1303
1681
  });
1304
- });
1305
- }))(),
1306
- timeoutPromise
1307
- ]);
1682
+ }),
1683
+ this.page.waitForLoadState("domcontentloaded"),
1684
+ this.page.waitForSelector("body"),
1685
+ timeoutPromise
1686
+ ]);
1687
+ } finally {
1688
+ clearTimeout(timeoutHandle);
1689
+ }
1308
1690
  } catch (e) {
1309
1691
  this.log({
1310
1692
  category: "dom",
@@ -1374,14 +1756,16 @@ Trace: ${e.stack}`,
1374
1756
  progress = "",
1375
1757
  content = {},
1376
1758
  chunksSeen = [],
1377
- modelName
1759
+ modelName,
1760
+ requestId,
1761
+ domSettleTimeoutMs
1378
1762
  }) {
1379
1763
  this.log({
1380
1764
  category: "extraction",
1381
1765
  message: `starting extraction '${instruction}'`,
1382
1766
  level: 1
1383
1767
  });
1384
- yield this._waitForSettledDom();
1768
+ yield this._waitForSettledDom(domSettleTimeoutMs);
1385
1769
  yield this.startDomDebug();
1386
1770
  const { outputString, chunk, chunks } = yield this.page.evaluate(
1387
1771
  (chunksSeen2) => window.processDom(chunksSeen2 != null ? chunksSeen2 : []),
@@ -1401,7 +1785,8 @@ Trace: ${e.stack}`,
1401
1785
  schema,
1402
1786
  modelName: modelName || this.defaultModelName,
1403
1787
  chunksSeen: chunksSeen.length,
1404
- chunksTotal: chunks.length
1788
+ chunksTotal: chunks.length,
1789
+ requestId
1405
1790
  });
1406
1791
  const _a = extractionResponse, {
1407
1792
  metadata: { progress: newProgress, completed }
@@ -1428,14 +1813,15 @@ Trace: ${e.stack}`,
1428
1813
  message: `continuing extraction, progress: '${newProgress}'`,
1429
1814
  level: 1
1430
1815
  });
1431
- yield this._waitForSettledDom();
1816
+ yield this._waitForSettledDom(domSettleTimeoutMs);
1432
1817
  return this._extract({
1433
1818
  instruction,
1434
1819
  schema,
1435
1820
  progress: newProgress,
1436
1821
  content: output,
1437
1822
  chunksSeen,
1438
- modelName
1823
+ modelName,
1824
+ domSettleTimeoutMs
1439
1825
  });
1440
1826
  }
1441
1827
  });
@@ -1445,7 +1831,9 @@ Trace: ${e.stack}`,
1445
1831
  instruction,
1446
1832
  useVision,
1447
1833
  fullPage,
1448
- modelName
1834
+ modelName,
1835
+ requestId,
1836
+ domSettleTimeoutMs
1449
1837
  }) {
1450
1838
  if (!instruction) {
1451
1839
  instruction = `Find elements that can be used for any future actions in the page. These may be navigation links, related pages, section/subsection links, buttons, or other interactive elements. Be comprehensive: if there are multiple elements that may be relevant for future actions, return all of them.`;
@@ -1456,7 +1844,7 @@ Trace: ${e.stack}`,
1456
1844
  message: `starting observation: ${instruction}`,
1457
1845
  level: 1
1458
1846
  });
1459
- yield this._waitForSettledDom();
1847
+ yield this._waitForSettledDom(domSettleTimeoutMs);
1460
1848
  yield this.startDomDebug();
1461
1849
  let { outputString, selectorMap } = yield this.page.evaluate(
1462
1850
  (fullPage2) => fullPage2 ? window.processAllOfDom() : window.processDom([]),
@@ -1485,7 +1873,8 @@ Trace: ${e.stack}`,
1485
1873
  domElements: outputString,
1486
1874
  llmProvider: this.llmProvider,
1487
1875
  modelName: modelName || this.defaultModelName,
1488
- image: annotatedScreenshot
1876
+ image: annotatedScreenshot,
1877
+ requestId
1489
1878
  });
1490
1879
  const elementsWithSelectors = observationResponse.elements.map(
1491
1880
  (element) => {
@@ -1514,7 +1903,9 @@ Trace: ${e.stack}`,
1514
1903
  modelName,
1515
1904
  useVision,
1516
1905
  verifierUseVision,
1517
- retries = 0
1906
+ retries = 0,
1907
+ requestId,
1908
+ domSettleTimeoutMs
1518
1909
  }) {
1519
1910
  var _a;
1520
1911
  const model = modelName != null ? modelName : this.defaultModelName;
@@ -1532,7 +1923,7 @@ Trace: ${e.stack}`,
1532
1923
  message: `Running / Continuing action: ${action} on page: ${this.page.url()}`,
1533
1924
  level: 2
1534
1925
  });
1535
- yield this._waitForSettledDom();
1926
+ yield this._waitForSettledDom(domSettleTimeoutMs);
1536
1927
  yield this.startDomDebug();
1537
1928
  this.log({
1538
1929
  category: "action",
@@ -1574,7 +1965,8 @@ Trace: ${e.stack}`,
1574
1965
  llmProvider: this.llmProvider,
1575
1966
  modelName: model,
1576
1967
  screenshot: annotatedScreenshot,
1577
- logger: this.logger
1968
+ logger: this.logger,
1969
+ requestId
1578
1970
  });
1579
1971
  this.log({
1580
1972
  category: "action",
@@ -1596,7 +1988,9 @@ Trace: ${e.stack}`,
1596
1988
  chunksSeen,
1597
1989
  modelName,
1598
1990
  useVision,
1599
- verifierUseVision
1991
+ verifierUseVision,
1992
+ requestId,
1993
+ domSettleTimeoutMs
1600
1994
  });
1601
1995
  } else if (useVision === "fallback") {
1602
1996
  this.log({
@@ -1611,9 +2005,14 @@ Trace: ${e.stack}`,
1611
2005
  chunksSeen,
1612
2006
  modelName,
1613
2007
  useVision: true,
1614
- verifierUseVision
2008
+ verifierUseVision,
2009
+ requestId,
2010
+ domSettleTimeoutMs
1615
2011
  });
1616
2012
  } else {
2013
+ if (this.enableCaching) {
2014
+ this.llmProvider.cleanRequestCache(requestId);
2015
+ }
1617
2016
  return {
1618
2017
  success: false,
1619
2018
  message: `Action was not able to be completed.`,
@@ -1670,7 +2069,9 @@ Trace: ${e.stack}`,
1670
2069
  useVision,
1671
2070
  verifierUseVision,
1672
2071
  retries: retries + 1,
1673
- chunksSeen
2072
+ chunksSeen,
2073
+ requestId,
2074
+ domSettleTimeoutMs
1674
2075
  });
1675
2076
  }
1676
2077
  }
@@ -1699,7 +2100,9 @@ Trace: ${e.stack}`,
1699
2100
  useVision,
1700
2101
  verifierUseVision,
1701
2102
  retries: retries + 1,
1702
- chunksSeen
2103
+ chunksSeen,
2104
+ requestId,
2105
+ domSettleTimeoutMs
1703
2106
  });
1704
2107
  }
1705
2108
  }
@@ -1722,7 +2125,9 @@ Trace: ${e.stack}`,
1722
2125
  useVision,
1723
2126
  verifierUseVision,
1724
2127
  retries: retries + 1,
1725
- chunksSeen
2128
+ chunksSeen,
2129
+ requestId,
2130
+ domSettleTimeoutMs
1726
2131
  });
1727
2132
  }
1728
2133
  }
@@ -1751,7 +2156,9 @@ Trace: ${e.stack}`,
1751
2156
  useVision,
1752
2157
  verifierUseVision,
1753
2158
  retries: retries + 1,
1754
- chunksSeen
2159
+ chunksSeen,
2160
+ requestId,
2161
+ domSettleTimeoutMs
1755
2162
  });
1756
2163
  }
1757
2164
  }
@@ -1781,7 +2188,7 @@ Trace: ${e.stack}`,
1781
2188
  yield newOpenedTab.close();
1782
2189
  yield this.page.goto(newOpenedTab.url());
1783
2190
  yield this.page.waitForLoadState("domcontentloaded");
1784
- yield this._waitForSettledDom();
2191
+ yield this._waitForSettledDom(domSettleTimeoutMs);
1785
2192
  }
1786
2193
  yield Promise.race([
1787
2194
  this.page.waitForLoadState("networkidle"),
@@ -1820,9 +2227,14 @@ Trace: ${e.stack}`,
1820
2227
  useVision,
1821
2228
  verifierUseVision,
1822
2229
  retries: retries + 1,
1823
- chunksSeen
2230
+ chunksSeen,
2231
+ requestId,
2232
+ domSettleTimeoutMs
1824
2233
  });
1825
2234
  } else {
2235
+ if (this.enableCaching) {
2236
+ this.llmProvider.cleanRequestCache(requestId);
2237
+ }
1826
2238
  return {
1827
2239
  success: false,
1828
2240
  message: `Internal error: Chosen method ${method} is invalid`,
@@ -1889,7 +2301,8 @@ Trace: ${e.stack}`,
1889
2301
  modelName: model,
1890
2302
  screenshot: fullpageScreenshot,
1891
2303
  domElements,
1892
- logger: this.logger
2304
+ logger: this.logger,
2305
+ requestId
1893
2306
  });
1894
2307
  this.log({
1895
2308
  category: "action",
@@ -1909,7 +2322,9 @@ Trace: ${e.stack}`,
1909
2322
  modelName,
1910
2323
  chunksSeen,
1911
2324
  useVision,
1912
- verifierUseVision
2325
+ verifierUseVision,
2326
+ requestId,
2327
+ domSettleTimeoutMs
1913
2328
  });
1914
2329
  } else {
1915
2330
  this.log({
@@ -1939,10 +2354,15 @@ Trace: ${error.stack}`,
1939
2354
  useVision,
1940
2355
  verifierUseVision,
1941
2356
  retries: retries + 1,
1942
- chunksSeen
2357
+ chunksSeen,
2358
+ requestId,
2359
+ domSettleTimeoutMs
1943
2360
  });
1944
2361
  }
1945
2362
  yield this._recordAction(action, "");
2363
+ if (this.enableCaching) {
2364
+ this.llmProvider.cleanRequestCache(requestId);
2365
+ }
1946
2366
  return {
1947
2367
  success: false,
1948
2368
  message: `Error performing action: ${error.message}`,
@@ -1955,15 +2375,37 @@ Trace: ${error.stack}`,
1955
2375
  return __async(this, arguments, function* ({
1956
2376
  action,
1957
2377
  modelName,
1958
- useVision = "fallback"
2378
+ useVision = "fallback",
2379
+ domSettleTimeoutMs
1959
2380
  }) {
1960
2381
  useVision = useVision != null ? useVision : "fallback";
2382
+ const requestId = Math.random().toString(36).substring(2);
2383
+ this.logger({
2384
+ category: "act",
2385
+ message: `Running act with action: ${action}, requestId: ${requestId}`
2386
+ });
1961
2387
  return this._act({
1962
2388
  action,
1963
2389
  modelName,
1964
2390
  chunksSeen: [],
1965
2391
  useVision,
1966
- verifierUseVision: useVision !== false
2392
+ verifierUseVision: useVision !== false,
2393
+ requestId,
2394
+ domSettleTimeoutMs
2395
+ }).catch((e) => {
2396
+ this.logger({
2397
+ category: "act",
2398
+ message: `Error acting: ${e.message}
2399
+ Trace: ${e.stack}`
2400
+ });
2401
+ if (this.enableCaching) {
2402
+ this.llmProvider.cleanRequestCache(requestId);
2403
+ }
2404
+ return {
2405
+ success: false,
2406
+ message: `Internal error: Error acting: ${e.message}`,
2407
+ action
2408
+ };
1967
2409
  });
1968
2410
  });
1969
2411
  }
@@ -1971,23 +2413,58 @@ Trace: ${error.stack}`,
1971
2413
  return __async(this, arguments, function* ({
1972
2414
  instruction,
1973
2415
  schema,
1974
- modelName
2416
+ modelName,
2417
+ domSettleTimeoutMs
1975
2418
  }) {
2419
+ const requestId = Math.random().toString(36).substring(2);
2420
+ this.logger({
2421
+ category: "extract",
2422
+ message: `Running extract with instruction: ${instruction}, requestId: ${requestId}`
2423
+ });
1976
2424
  return this._extract({
1977
2425
  instruction,
1978
2426
  schema,
1979
- modelName
2427
+ modelName,
2428
+ requestId,
2429
+ domSettleTimeoutMs
2430
+ }).catch((e) => {
2431
+ this.logger({
2432
+ category: "extract",
2433
+ message: `Internal error: Error extracting: ${e.message}
2434
+ Trace: ${e.stack}`
2435
+ });
2436
+ if (this.enableCaching) {
2437
+ this.llmProvider.cleanRequestCache(requestId);
2438
+ }
2439
+ throw e;
1980
2440
  });
1981
2441
  });
1982
2442
  }
1983
2443
  observe(options) {
1984
2444
  return __async(this, null, function* () {
1985
2445
  var _a, _b;
2446
+ const requestId = Math.random().toString(36).substring(2);
2447
+ this.logger({
2448
+ category: "observe",
2449
+ message: `Running observe with instruction: ${options == null ? void 0 : options.instruction}, requestId: ${requestId}`
2450
+ });
1986
2451
  return this._observe({
1987
2452
  instruction: (_a = options == null ? void 0 : options.instruction) != null ? _a : "Find actions that can be performed on this page.",
1988
2453
  modelName: options == null ? void 0 : options.modelName,
1989
2454
  useVision: (_b = options == null ? void 0 : options.useVision) != null ? _b : false,
1990
- fullPage: false
2455
+ fullPage: false,
2456
+ requestId,
2457
+ domSettleTimeoutMs: options == null ? void 0 : options.domSettleTimeoutMs
2458
+ }).catch((e) => {
2459
+ this.logger({
2460
+ category: "observe",
2461
+ message: `Error observing: ${e.message}
2462
+ Trace: ${e.stack}`
2463
+ });
2464
+ if (this.enableCaching) {
2465
+ this.llmProvider.cleanRequestCache(requestId);
2466
+ }
2467
+ throw e;
1991
2468
  });
1992
2469
  });
1993
2470
  }