frdel commited on
Commit
90c95b4
·
1 Parent(s): f3f8ca5

merge prep

Browse files
models.py CHANGED
@@ -33,6 +33,7 @@ from langchain_core.messages import (
33
  from langchain.embeddings.base import Embeddings
34
 
35
  load_dotenv()
 
36
 
37
 
38
  class ModelType(Enum):
 
33
  from langchain.embeddings.base import Embeddings
34
 
35
  load_dotenv()
36
+ os.environ['LITELLM_LOG'] = "ERROR" # only errors
37
 
38
 
39
  class ModelType(Enum):
prompts/default/browser_agent.system.md CHANGED
@@ -4,14 +4,9 @@ Follow instructions as closely as possible
4
  When told go to website, open the website. If no other instructions: stop there
5
  Do not interact with the website unless told to
6
  Always accept all cookies if prompted on the website, NEVER go to browser cookie settings
7
- In page_summary respond with one paragraph of main content plus an overview of page elements
8
  If asked specific questions about a website, be as precise and close to the actual page content as possible
9
  If you are waiting for instructions: you should end the task and mark as done
10
 
11
- ## Response Format
12
- Your responses must always be formatted as a JSON object
13
- The response JSON must contain at least the following fields: "title", "response", "page_summary"
14
-
15
  ## Task Completion
16
  When you have completed the assigned task OR are waiting for further instructions:
17
  1. Use the "Complete task" action to mark the task as complete
@@ -20,18 +15,8 @@ When you have completed the assigned task OR are waiting for further instruction
20
 
21
  ## Important Notes
22
  - Always call "Complete task" when your objective is achieved
 
 
23
  - If you navigate to a website and no further actions are requested, call "Complete task" immediately
24
  - If you complete any requested interaction (clicking, typing, etc.), call "Complete task"
25
- - Never leave a task running indefinitely - always conclude with "Complete task"
26
-
27
- ## Response fields
28
- * title (type: str) - The ttitle of the current web page
29
- * response (type: str) - Your response to your superior's last request
30
- * page_summary (type: str) - Summary of the current web page as requested by superior
31
-
32
- ## Example response
33
- {
34
- "title": "Google Search",
35
- "response": "I have succesfully navigated to the response page.",
36
- "page_summary": "The page contains a menu bar with ... and a search input field. Under the search field there are two buttons with ... and ..."
37
- }
 
4
  When told go to website, open the website. If no other instructions: stop there
5
  Do not interact with the website unless told to
6
  Always accept all cookies if prompted on the website, NEVER go to browser cookie settings
 
7
  If asked specific questions about a website, be as precise and close to the actual page content as possible
8
  If you are waiting for instructions: you should end the task and mark as done
9
 
 
 
 
 
10
  ## Task Completion
11
  When you have completed the assigned task OR are waiting for further instructions:
12
  1. Use the "Complete task" action to mark the task as complete
 
15
 
16
  ## Important Notes
17
  - Always call "Complete task" when your objective is achieved
18
+ - In page_summary respond with one paragraph of main content plus an overview of page elements
19
+ - Response field is used to answer to user's task or ask additional questions
20
  - If you navigate to a website and no further actions are requested, call "Complete task" immediately
21
  - If you complete any requested interaction (clicking, typing, etc.), call "Complete task"
22
+ - Never leave a task running indefinitely - always conclude with "Complete task"
 
 
 
 
 
 
 
 
 
 
 
 
python/api/image_get.py CHANGED
@@ -1,34 +1,40 @@
1
  import os
2
  import re
 
3
  from python.helpers.api import ApiHandler
4
  from python.helpers import files
5
  from flask import Request, Response, send_file
6
 
7
 
8
  class ImageGet(ApiHandler):
 
 
 
 
 
9
  async def process(self, input: dict, request: Request) -> dict | Response:
10
- # input data
11
- path = input.get("path", request.args.get("path", ""))
12
- if not path:
13
- raise ValueError("No path provided")
14
-
15
- # check if path is within base directory
16
- if not files.is_in_base_dir(path):
17
- raise ValueError("Path is outside of allowed directory")
18
-
19
- # check if file has an image extension
20
- # list of allowed image extensions
21
- allowed_extensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg"]
22
- # get file extension
23
- file_ext = os.path.splitext(path)[1].lower()
24
- if file_ext not in allowed_extensions:
25
- raise ValueError(f"File type not allowed. Allowed types: {', '.join(allowed_extensions)}")
26
-
27
- # check if file exists
28
- if not os.path.exists(path):
29
- raise ValueError("File not found")
30
-
31
- # send file
32
- return send_file(path)
33
-
34
-
 
1
  import os
2
  import re
3
+ from typing import override
4
  from python.helpers.api import ApiHandler
5
  from python.helpers import files
6
  from flask import Request, Response, send_file
7
 
8
 
9
  class ImageGet(ApiHandler):
10
+
11
+ @classmethod
12
+ def get_methods(cls) -> list[str]:
13
+ return ["GET"]
14
+
15
  async def process(self, input: dict, request: Request) -> dict | Response:
16
+ # input data
17
+ path = input.get("path", request.args.get("path", ""))
18
+ if not path:
19
+ raise ValueError("No path provided")
20
+
21
+ # check if path is within base directory
22
+ if not files.is_in_base_dir(path):
23
+ raise ValueError("Path is outside of allowed directory")
24
+
25
+ # check if file has an image extension
26
+ # list of allowed image extensions
27
+ allowed_extensions = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg"]
28
+ # get file extension
29
+ file_ext = os.path.splitext(path)[1].lower()
30
+ if file_ext not in allowed_extensions:
31
+ raise ValueError(
32
+ f"File type not allowed. Allowed types: {', '.join(allowed_extensions)}"
33
+ )
34
+
35
+ # check if file exists
36
+ if not os.path.exists(path):
37
+ raise ValueError("File not found")
38
+
39
+ # send file
40
+ return send_file(path)
python/tools/code_execution_tool.py CHANGED
@@ -219,7 +219,7 @@ class CodeExecution(Tool):
219
  while True:
220
  await asyncio.sleep(sleep_time)
221
  full_output, partial_output = await self.state.shells[session].read_output(
222
- timeout=3, reset_full_output=reset_full_output
223
  )
224
  reset_full_output = False # only reset once
225
 
 
219
  while True:
220
  await asyncio.sleep(sleep_time)
221
  full_output, partial_output = await self.state.shells[session].read_output(
222
+ timeout=1, reset_full_output=reset_full_output
223
  )
224
  reset_full_output = False # only reset once
225
 
run_ui.py CHANGED
@@ -131,7 +131,9 @@ def csrf_protect(f):
131
  async def decorated(*args, **kwargs):
132
  token = session.get("csrf_token")
133
  header = request.headers.get("X-CSRF-Token")
134
- if not token or not header or token != header:
 
 
135
  return Response("CSRF token missing or invalid", 403)
136
  return await f(*args, **kwargs)
137
 
 
131
  async def decorated(*args, **kwargs):
132
  token = session.get("csrf_token")
133
  header = request.headers.get("X-CSRF-Token")
134
+ cookie = request.cookies.get("csrf_token")
135
+ sent = header or cookie
136
+ if not token or not sent or token != sent:
137
  return Response("CSRF token missing or invalid", 403)
138
  return await f(*args, **kwargs)
139
 
webui/components/messages/resize/message-resize-store.js ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createStore } from "/js/AlpineStore.js";
2
+ import { toggleCssProperty } from "/js/css.js";
3
+
4
+ const model = {
5
+ settings: {},
6
+
7
+ async init() {
8
+ this.settings =
9
+ JSON.parse(localStorage.getItem("messageResizeSettings") || "null") ||
10
+ this._getDefaultSettings();
11
+ this._applyAllSettings();
12
+ },
13
+
14
+ _getDefaultSettings() {
15
+ return {
16
+ "message-agent": { minimized: true, maximized: false },
17
+ "messsage-code-exec": { minimized: false, maximized: false },
18
+ };
19
+ },
20
+
21
+ _getSetting(className) {
22
+ return this.settings[className] || { minimized: false, maximized: false };
23
+ },
24
+
25
+ _getDefaultSetting() {
26
+ return { minimized: false, maximized: false };
27
+ },
28
+
29
+ _setSetting(className, setting) {
30
+ this.settings[className] = setting;
31
+ localStorage.setItem(
32
+ "messageResizeSettings",
33
+ JSON.stringify(this.settings)
34
+ );
35
+ },
36
+
37
+ _applyAllSettings() {
38
+ for (const [className, setting] of Object.entries(this.settings)) {
39
+ this._applySetting(className, setting);
40
+ }
41
+ },
42
+
43
+ async minimizeMessageClass(className, event) {
44
+ const set = this._getSetting(className);
45
+ set.minimized = !set.minimized;
46
+ this._setSetting(className, set);
47
+ this._applySetting(className, set);
48
+ this._applyScroll(event);
49
+ },
50
+
51
+ async maximizeMessageClass(className, event) {
52
+ const set = this._getSetting(className);
53
+ if (set.minimized) return this.minimizeMessageClass(className, event); // if minimized, unminimize first
54
+ set.maximized = !set.maximized;
55
+ this._setSetting(className, set);
56
+ this._applySetting(className, set);
57
+ this._applyScroll(event);
58
+ },
59
+
60
+ _applyScroll(event) {
61
+ event.target.scrollIntoView({ behavior: "smooth" });
62
+ },
63
+
64
+ _applySetting(className, setting) {
65
+ toggleCssProperty(
66
+ `.${className} .message-body`,
67
+ "max-height",
68
+ setting.maximized ? undefined : "30em"
69
+ );
70
+ toggleCssProperty(
71
+ `.${className} .message-body`,
72
+ "overflow-y",
73
+ setting.maximized ? undefined : "auto"
74
+ );
75
+ toggleCssProperty(
76
+ `.${className} .message-body`,
77
+ "display",
78
+ setting.minimized ? "none" : "block"
79
+ );
80
+ },
81
+ };
82
+
83
+ const store = createStore("messageResize", model);
84
+
85
+ export { store };
webui/index.css CHANGED
@@ -689,6 +689,18 @@ pre {
689
  background-color: #4b3a69;
690
  }
691
 
 
 
 
 
 
 
 
 
 
 
 
 
692
  .message-browser {
693
  background-color: #4b3a69;
694
  }
@@ -725,6 +737,11 @@ pre {
725
  width: 100%;
726
  }
727
 
 
 
 
 
 
728
  .msg-kvps th,
729
  .msg-kvps td {
730
  align-content: center;
@@ -744,6 +761,9 @@ pre {
744
 
745
  .msg-heading {
746
  margin: 0;
 
 
 
747
  }
748
 
749
  /* Message Actions */
@@ -1735,6 +1755,9 @@ input:checked + .slider:before {
1735
 
1736
  .kvps-val {
1737
  margin: 0 0 0.4rem 0;
 
 
 
1738
  }
1739
  }
1740
 
@@ -3081,3 +3104,12 @@ nav ul li a img {
3081
  border: 1px solid rgba(142, 142, 142, 0.1);
3082
  border-radius: 0.3em;
3083
  }
 
 
 
 
 
 
 
 
 
 
689
  background-color: #4b3a69;
690
  }
691
 
692
+ .message-code-exe .message-body {
693
+ min-height: 5em;
694
+ background-color: var(--color-panel);
695
+ border-radius: 0.5em;
696
+ margin: 0.3em;
697
+ padding: 0.3em;
698
+ }
699
+
700
+ .light-mode .message-code-exe .message-body {
701
+ border: 1px solid var(--color-border);
702
+ }
703
+
704
  .message-browser {
705
  background-color: #4b3a69;
706
  }
 
737
  width: 100%;
738
  }
739
 
740
+ .kvps-val pre {
741
+ white-space: pre-wrap; /* keep \n, collapse no spaces, allow wrapping */
742
+ word-break: break-word; /* optional – forces really long “words” to break */
743
+ }
744
+
745
  .msg-kvps th,
746
  .msg-kvps td {
747
  align-content: center;
 
761
 
762
  .msg-heading {
763
  margin: 0;
764
+ padding-right: 5.5em; /* space for buttons */
765
+ position: relative;
766
+ display: block;
767
  }
768
 
769
  /* Message Actions */
 
1755
 
1756
  .kvps-val {
1757
  margin: 0 0 0.4rem 0;
1758
+ white-space: pre-wrap;
1759
+ word-break: break-word;
1760
+ overflow-wrap: anywhere;
1761
  }
1762
  }
1763
 
 
3104
  border: 1px solid rgba(142, 142, 142, 0.1);
3105
  border-radius: 0.3em;
3106
  }
3107
+
3108
+ .msg-min-max-btns {
3109
+ position: absolute;
3110
+ top: -0.2em;
3111
+ right: -0.5em;
3112
+ display: flex;
3113
+ gap: 0.3em;
3114
+ z-index: 1;
3115
+ }
webui/index.html CHANGED
@@ -118,6 +118,10 @@
118
  <script defer src="vendor/katex/katex.auto-render.min.js"
119
  crossorigin="anonymous"></script>
120
 
 
 
 
 
121
  <!-- Non-module scripts after Alpine.js -->
122
  <script type="text/javascript" src="js/settings.js"></script>
123
  <script type="text/javascript" src="js/file_browser.js"></script>
 
118
  <script defer src="vendor/katex/katex.auto-render.min.js"
119
  crossorigin="anonymous"></script>
120
 
121
+ <!-- Google Icons -->
122
+ <link rel="stylesheet" href="vendor/google/google-icons.css" />
123
+
124
+
125
  <!-- Non-module scripts after Alpine.js -->
126
  <script type="text/javascript" src="js/settings.js"></script>
127
  <script type="text/javascript" src="js/file_browser.js"></script>
webui/js/api.js CHANGED
@@ -79,5 +79,6 @@ async function getCsrfToken() {
79
  credentials: "same-origin",
80
  }).then((r) => r.json());
81
  csrfToken = response.token;
 
82
  return csrfToken;
83
- }
 
79
  credentials: "same-origin",
80
  }).then((r) => r.json());
81
  csrfToken = response.token;
82
+ document.cookie = "csrf_token=" + csrfToken + "; SameSite=Strict; Path=/";
83
  return csrfToken;
84
+ }
webui/js/css.js ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Create and keep a reference to a dynamic stylesheet for runtime CSS changes
2
+ let dynamicStyleSheet;
3
+ {
4
+ const style = document.createElement("style");
5
+ style.appendChild(document.createTextNode(""));
6
+ document.head.appendChild(style);
7
+ dynamicStyleSheet = style.sheet;
8
+ }
9
+
10
+ export function toggleCssProperty(selector, property, value) {
11
+ // Get the stylesheet that contains the class
12
+ const styleSheets = document.styleSheets;
13
+
14
+ // Iterate through all stylesheets to find the class
15
+ for (let i = 0; i < styleSheets.length; i++) {
16
+ const styleSheet = styleSheets[i];
17
+ let rules;
18
+ try {
19
+ rules = styleSheet.cssRules || styleSheet.rules;
20
+ } catch (e) {
21
+ // Skip stylesheets we cannot access due to CORS/security restrictions
22
+ continue;
23
+ }
24
+ if (!rules) continue;
25
+
26
+ for (let j = 0; j < rules.length; j++) {
27
+ const rule = rules[j];
28
+ if (rule.selectorText == selector) {
29
+ _applyCssToRule(rule, property, value);
30
+ return;
31
+ }
32
+ }
33
+ }
34
+ // If not found, add it to the dynamic stylesheet
35
+ const ruleIndex = dynamicStyleSheet.insertRule(
36
+ `${selector} {}`,
37
+ dynamicStyleSheet.cssRules.length
38
+ );
39
+ const rule = dynamicStyleSheet.cssRules[ruleIndex];
40
+ _applyCssToRule(rule, property, value);
41
+ }
42
+
43
+ // Helper to apply/remove a CSS property on a rule
44
+ function _applyCssToRule(rule, property, value) {
45
+ if (value === undefined) {
46
+ rule.style.removeProperty(property);
47
+ } else {
48
+ rule.style.setProperty(property, value);
49
+ }
50
+ }
webui/js/messages.js CHANGED
@@ -1,6 +1,7 @@
1
  // copy button
2
  import { openImageModal } from "./image_modal.js";
3
  import { marked } from "../vendor/marked/marked.esm.js";
 
4
 
5
  function createCopyButton() {
6
  const button = document.createElement("button");
@@ -81,23 +82,39 @@ export function _drawMessage(
81
  content,
82
  temp,
83
  followUp,
 
84
  kvps = null,
85
  messageClasses = [],
86
  contentClasses = [],
87
  latex = false,
88
- markdown = false
 
89
  ) {
90
  const messageDiv = document.createElement("div");
91
- messageDiv.classList.add("message", ...messageClasses);
92
 
93
  if (heading) {
94
  const headingElement = document.createElement("h4");
95
  headingElement.classList.add("msg-heading");
96
  headingElement.textContent = heading;
97
  messageDiv.appendChild(headingElement);
 
 
 
 
 
 
 
 
 
 
98
  }
99
 
100
- drawKvps(messageDiv, kvps, false);
 
 
 
 
101
 
102
  if (content && content.trim().length > 0) {
103
  if (markdown) {
@@ -124,7 +141,7 @@ export function _drawMessage(
124
 
125
  contentDiv.appendChild(spanElement);
126
  addCopyButtonToElement(contentDiv);
127
- messageDiv.appendChild(contentDiv);
128
  } else {
129
  const preElement = document.createElement("pre");
130
  preElement.classList.add("msg-content", ...contentClasses);
@@ -141,7 +158,7 @@ export function _drawMessage(
141
 
142
  preElement.appendChild(spanElement);
143
  addCopyButtonToElement(preElement);
144
- messageDiv.appendChild(preElement);
145
  }
146
  }
147
 
@@ -151,6 +168,11 @@ export function _drawMessage(
151
  messageContainer.classList.add("message-followup");
152
  }
153
 
 
 
 
 
 
154
  return messageDiv;
155
  }
156
 
@@ -169,8 +191,9 @@ export function drawMessageDefault(
169
  content,
170
  temp,
171
  false,
 
172
  kvps,
173
- ["message-ai", "message-default"],
174
  ["msg-json"],
175
  false,
176
  false
@@ -198,8 +221,9 @@ export function drawMessageAgent(
198
  content,
199
  temp,
200
  false,
 
201
  kvpsFlat,
202
- ["message-ai", "message-agent"],
203
  ["msg-json"],
204
  false,
205
  false
@@ -221,8 +245,9 @@ export function drawMessageResponse(
221
  content,
222
  temp,
223
  true,
 
224
  null,
225
- ["message-ai", "message-agent-response"],
226
  [],
227
  true,
228
  true
@@ -244,8 +269,9 @@ export function drawMessageDelegation(
244
  content,
245
  temp,
246
  true,
 
247
  kvps,
248
- ["message-ai", "message-agent", "message-agent-delegation"],
249
  [],
250
  true,
251
  false
@@ -364,8 +390,9 @@ export function drawMessageTool(
364
  content,
365
  temp,
366
  true,
 
367
  kvps,
368
- ["message-ai", "message-tool"],
369
  ["msg-output"],
370
  false,
371
  false
@@ -387,8 +414,9 @@ export function drawMessageCodeExe(
387
  content,
388
  temp,
389
  true,
 
390
  null,
391
- ["message-ai", "message-code-exe"],
392
  [],
393
  false,
394
  false
@@ -410,8 +438,9 @@ export function drawMessageBrowser(
410
  content,
411
  temp,
412
  true,
 
413
  kvps,
414
- ["message-ai", "message-browser"],
415
  ["msg-json"],
416
  false,
417
  false
@@ -419,7 +448,7 @@ export function drawMessageBrowser(
419
  }
420
 
421
  export function drawMessageAgentPlain(
422
- classes,
423
  messageContainer,
424
  id,
425
  type,
@@ -434,8 +463,9 @@ export function drawMessageAgentPlain(
434
  content,
435
  temp,
436
  false,
 
437
  kvps,
438
- [...classes],
439
  [],
440
  false,
441
  false
@@ -453,7 +483,7 @@ export function drawMessageInfo(
453
  kvps = null
454
  ) {
455
  return drawMessageAgentPlain(
456
- ["message-info"],
457
  messageContainer,
458
  id,
459
  type,
@@ -479,8 +509,9 @@ export function drawMessageUtil(
479
  content,
480
  temp,
481
  false,
 
482
  kvps,
483
- ["message-util"],
484
  ["msg-json"],
485
  false,
486
  false
@@ -498,7 +529,7 @@ export function drawMessageWarning(
498
  kvps = null
499
  ) {
500
  return drawMessageAgentPlain(
501
- ["message-warning"],
502
  messageContainer,
503
  id,
504
  type,
@@ -519,7 +550,7 @@ export function drawMessageError(
519
  kvps = null
520
  ) {
521
  return drawMessageAgentPlain(
522
- ["message-error"],
523
  messageContainer,
524
  id,
525
  type,
@@ -537,7 +568,8 @@ function drawKvps(container, kvps, latex) {
537
  for (let [key, value] of Object.entries(kvps)) {
538
  const row = table.insertRow();
539
  row.classList.add("kvps-row");
540
- if (key === "thoughts" || key === "reasoning") // TODO: find a better way to determine special class assignment
 
541
  row.classList.add("msg-thoughts");
542
 
543
  const th = row.insertCell();
@@ -557,6 +589,11 @@ function drawKvps(container, kvps, latex) {
557
  addValue(value);
558
  }
559
 
 
 
 
 
 
560
  function addValue(value) {
561
  if (typeof value === "object") value = JSON.stringify(value, null, 2);
562
 
@@ -572,7 +609,6 @@ function drawKvps(container, kvps, latex) {
572
  imgElement.addEventListener("click", () => {
573
  openImageModal(imgElement.src, 1000);
574
  });
575
-
576
  } else {
577
  const pre = document.createElement("pre");
578
  // pre.classList.add("kvps-val");
@@ -588,14 +624,14 @@ function drawKvps(container, kvps, latex) {
588
  copyText(span.textContent, span);
589
  });
590
 
591
- // KaTeX rendering for markdown
592
- if (latex) {
593
- span.querySelectorAll("latex").forEach((element) => {
594
- katex.render(element.innerHTML, element, {
595
- throwOnError: false,
596
- });
597
- });
598
- }
599
  }
600
  }
601
  // } else {
 
1
  // copy button
2
  import { openImageModal } from "./image_modal.js";
3
  import { marked } from "../vendor/marked/marked.esm.js";
4
+ import { store as messageResizeStore } from "/components/messages/resize/message-resize-store.js";
5
 
6
  function createCopyButton() {
7
  const button = document.createElement("button");
 
82
  content,
83
  temp,
84
  followUp,
85
+ mainClass = "",
86
  kvps = null,
87
  messageClasses = [],
88
  contentClasses = [],
89
  latex = false,
90
+ markdown = false,
91
+ resizeBtns = true
92
  ) {
93
  const messageDiv = document.createElement("div");
94
+ messageDiv.classList.add("message", mainClass, ...messageClasses);
95
 
96
  if (heading) {
97
  const headingElement = document.createElement("h4");
98
  headingElement.classList.add("msg-heading");
99
  headingElement.textContent = heading;
100
  messageDiv.appendChild(headingElement);
101
+
102
+ if (resizeBtns) {
103
+ const minMaxBtn = document.createElement("div");
104
+ minMaxBtn.classList.add("msg-min-max-btns");
105
+ minMaxBtn.innerHTML = `
106
+ <a href="#" class="msg-min-max-btn" @click.prevent="$store.messageResize.minimizeMessageClass('${mainClass}', $event)"><span class="material-symbols-outlined">minimize</span></a>
107
+ <a href="#" class="msg-min-max-btn" @click.prevent="$store.messageResize.maximizeMessageClass('${mainClass}', $event)"><span class="material-symbols-outlined">expand_all</span></a>
108
+ `;
109
+ headingElement.appendChild(minMaxBtn);
110
+ }
111
  }
112
 
113
+ const bodyDiv = document.createElement("div");
114
+ bodyDiv.classList.add("message-body");
115
+ messageDiv.appendChild(bodyDiv);
116
+
117
+ drawKvps(bodyDiv, kvps, false);
118
 
119
  if (content && content.trim().length > 0) {
120
  if (markdown) {
 
141
 
142
  contentDiv.appendChild(spanElement);
143
  addCopyButtonToElement(contentDiv);
144
+ bodyDiv.appendChild(contentDiv);
145
  } else {
146
  const preElement = document.createElement("pre");
147
  preElement.classList.add("msg-content", ...contentClasses);
 
158
 
159
  preElement.appendChild(spanElement);
160
  addCopyButtonToElement(preElement);
161
+ bodyDiv.appendChild(preElement);
162
  }
163
  }
164
 
 
168
  messageContainer.classList.add("message-followup");
169
  }
170
 
171
+ // autoscroll the body if needed
172
+ setTimeout(() => {
173
+ bodyDiv.scrollTop = bodyDiv.scrollHeight;
174
+ }, 0);
175
+
176
  return messageDiv;
177
  }
178
 
 
191
  content,
192
  temp,
193
  false,
194
+ "message-default",
195
  kvps,
196
+ ["message-ai"],
197
  ["msg-json"],
198
  false,
199
  false
 
221
  content,
222
  temp,
223
  false,
224
+ "message-agent",
225
  kvpsFlat,
226
+ ["message-ai"],
227
  ["msg-json"],
228
  false,
229
  false
 
245
  content,
246
  temp,
247
  true,
248
+ "message-agent-response",
249
  null,
250
+ ["message-ai"],
251
  [],
252
  true,
253
  true
 
269
  content,
270
  temp,
271
  true,
272
+ "message-agent-delegation",
273
  kvps,
274
+ ["message-ai", "message-agent"],
275
  [],
276
  true,
277
  false
 
390
  content,
391
  temp,
392
  true,
393
+ "message-tool",
394
  kvps,
395
+ ["message-ai"],
396
  ["msg-output"],
397
  false,
398
  false
 
414
  content,
415
  temp,
416
  true,
417
+ "message-code-exe",
418
  null,
419
+ ["message-ai"],
420
  [],
421
  false,
422
  false
 
438
  content,
439
  temp,
440
  true,
441
+ "message-browser",
442
  kvps,
443
+ ["message-ai"],
444
  ["msg-json"],
445
  false,
446
  false
 
448
  }
449
 
450
  export function drawMessageAgentPlain(
451
+ mainClass,
452
  messageContainer,
453
  id,
454
  type,
 
463
  content,
464
  temp,
465
  false,
466
+ mainClass,
467
  kvps,
468
+ [],
469
  [],
470
  false,
471
  false
 
483
  kvps = null
484
  ) {
485
  return drawMessageAgentPlain(
486
+ "message-info",
487
  messageContainer,
488
  id,
489
  type,
 
509
  content,
510
  temp,
511
  false,
512
+ "message-util",
513
  kvps,
514
+ [],
515
  ["msg-json"],
516
  false,
517
  false
 
529
  kvps = null
530
  ) {
531
  return drawMessageAgentPlain(
532
+ "message-warning",
533
  messageContainer,
534
  id,
535
  type,
 
550
  kvps = null
551
  ) {
552
  return drawMessageAgentPlain(
553
+ "message-error",
554
  messageContainer,
555
  id,
556
  type,
 
568
  for (let [key, value] of Object.entries(kvps)) {
569
  const row = table.insertRow();
570
  row.classList.add("kvps-row");
571
+ if (key === "thoughts" || key === "reasoning")
572
+ // TODO: find a better way to determine special class assignment
573
  row.classList.add("msg-thoughts");
574
 
575
  const th = row.insertCell();
 
589
  addValue(value);
590
  }
591
 
592
+ // autoscroll the KVP value if needed
593
+ setTimeout(() => {
594
+ tdiv.scrollTop = tdiv.scrollHeight;
595
+ }, 0);
596
+
597
  function addValue(value) {
598
  if (typeof value === "object") value = JSON.stringify(value, null, 2);
599
 
 
609
  imgElement.addEventListener("click", () => {
610
  openImageModal(imgElement.src, 1000);
611
  });
 
612
  } else {
613
  const pre = document.createElement("pre");
614
  // pre.classList.add("kvps-val");
 
624
  copyText(span.textContent, span);
625
  });
626
 
627
+ // KaTeX rendering for markdown
628
+ if (latex) {
629
+ span.querySelectorAll("latex").forEach((element) => {
630
+ katex.render(element.innerHTML, element, {
631
+ throwOnError: false,
632
+ });
633
+ });
634
+ }
635
  }
636
  }
637
  // } else {
webui/vendor/google/google-icons.css ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @font-face {
2
+ font-family: 'Material Symbols Outlined';
3
+ font-style: normal;
4
+ font-weight: 400;
5
+ src: url(./google-icons.ttf) format('truetype');
6
+ }
7
+
8
+ .material-symbols-outlined {
9
+ font-family: 'Material Symbols Outlined';
10
+ font-weight: normal;
11
+ font-style: normal;
12
+ font-size: 24px;
13
+ line-height: 1;
14
+ letter-spacing: normal;
15
+ text-transform: none;
16
+ display: inline-block;
17
+ white-space: nowrap;
18
+ word-wrap: normal;
19
+ direction: ltr;
20
+ }
webui/vendor/google/google-icons.ttf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:17d893530b09a73505a9f0358db6ac33c0584b7a6fce20157eceeccee0890830
3
+ size 867212