mindroot 7.7.0__py3-none-any.whl → 8.2.0__py3-none-any.whl

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.

Potentially problematic release.


This version of mindroot might be problematic. Click here for more details.

Files changed (35) hide show
  1. mindroot/coreplugins/admin/plugin_manager.py +126 -3
  2. mindroot/coreplugins/admin/plugin_manager_backup.py +615 -0
  3. mindroot/coreplugins/admin/router.py +3 -1
  4. mindroot/coreplugins/admin/server_router.py +8 -1
  5. mindroot/coreplugins/admin/static/js/plugin-advanced-install.js +83 -12
  6. mindroot/coreplugins/admin/static/js/plugin-index-browser.js +138 -10
  7. mindroot/coreplugins/admin/static/js/plugin-install-dialog.js +345 -0
  8. mindroot/coreplugins/admin/static/js/server-control.js +68 -6
  9. mindroot/coreplugins/agent/agent.py +4 -0
  10. mindroot/coreplugins/chat/models.py +0 -1
  11. mindroot/coreplugins/chat/router.py +31 -0
  12. mindroot/coreplugins/chat/services.py +24 -0
  13. mindroot/coreplugins/chat/static/css/dark.css +35 -0
  14. mindroot/coreplugins/chat/static/css/default.css +35 -0
  15. mindroot/coreplugins/chat/static/js/chatform.js +185 -0
  16. mindroot/coreplugins/env_manager/__init__.py +3 -0
  17. mindroot/coreplugins/env_manager/inject/admin.jinja2 +16 -0
  18. mindroot/coreplugins/env_manager/mod.py +228 -0
  19. mindroot/coreplugins/env_manager/router.py +40 -0
  20. mindroot/coreplugins/env_manager/static/css/env-manager.css +263 -0
  21. mindroot/coreplugins/env_manager/static/js/env-manager.js +380 -0
  22. mindroot/coreplugins/home/router.py +33 -2
  23. mindroot/coreplugins/home/static/css/enhanced.css +111 -5
  24. mindroot/coreplugins/home/templates/home.jinja2 +7 -4
  25. mindroot/lib/chatlog.py +5 -1
  26. mindroot/lib/streamcmd.py +139 -0
  27. mindroot/lib/templates.py +13 -2
  28. mindroot/server.py +12 -25
  29. mindroot-8.2.0.dist-info/METADATA +15 -0
  30. {mindroot-7.7.0.dist-info → mindroot-8.2.0.dist-info}/RECORD +34 -25
  31. {mindroot-7.7.0.dist-info → mindroot-8.2.0.dist-info}/WHEEL +1 -1
  32. mindroot-7.7.0.dist-info/METADATA +0 -310
  33. {mindroot-7.7.0.dist-info → mindroot-8.2.0.dist-info}/entry_points.txt +0 -0
  34. {mindroot-7.7.0.dist-info → mindroot-8.2.0.dist-info}/licenses/LICENSE +0 -0
  35. {mindroot-7.7.0.dist-info → mindroot-8.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,380 @@
1
+ import { LitElement, html, css } from '/admin/static/js/lit-core.min.js';
2
+ import { BaseEl } from '/admin/static/js/base.js';
3
+
4
+ class EnvManager extends BaseEl {
5
+ static properties = {
6
+ pluginData: { type: Object },
7
+ loading: { type: Boolean },
8
+ editingVar: { type: String },
9
+ newVarName: { type: String },
10
+ newVarValue: { type: String },
11
+ showAddForm: { type: Boolean },
12
+ searchTerm: { type: String }
13
+ };
14
+
15
+ constructor() {
16
+ super();
17
+ this.pluginData = null;
18
+ this.loading = true;
19
+ this.editingVar = null;
20
+ this.newVarName = '';
21
+ this.newVarValue = '';
22
+ this.showAddForm = false;
23
+ this.searchTerm = '';
24
+ }
25
+
26
+ connectedCallback() {
27
+ super.connectedCallback();
28
+ this.fetchEnvVars();
29
+ }
30
+
31
+ async fetchEnvVars() {
32
+ this.loading = true;
33
+ try {
34
+ const response = await fetch('/env_vars/scan');
35
+ const result = await response.json();
36
+ if (result.success) {
37
+ this.pluginData = result.data;
38
+ } else {
39
+ console.error('Error fetching environment variables:', result.error);
40
+ }
41
+ } catch (error) {
42
+ console.error('Error fetching environment variables:', error);
43
+ } finally {
44
+ this.loading = false;
45
+ }
46
+ }
47
+
48
+ async updateEnvVar(varName, varValue) {
49
+ try {
50
+ const response = await fetch('/env_vars/update', {
51
+ method: 'POST',
52
+ headers: {
53
+ 'Content-Type': 'application/json',
54
+ },
55
+ body: JSON.stringify({
56
+ var_name: varName,
57
+ var_value: varValue
58
+ })
59
+ });
60
+
61
+ const result = await response.json();
62
+ if (result.success) {
63
+ // Refresh the data
64
+ await this.fetchEnvVars();
65
+ return true;
66
+ } else {
67
+ console.error('Error updating environment variable:', result.message);
68
+ return false;
69
+ }
70
+ } catch (error) {
71
+ console.error('Error updating environment variable:', error);
72
+ return false;
73
+ }
74
+ }
75
+
76
+ handleEditClick(varName) {
77
+ this.editingVar = varName;
78
+ }
79
+
80
+ async handleSaveEdit(varName) {
81
+ const inputEl = this.shadowRoot.querySelector(`#edit-${varName}`);
82
+ if (inputEl) {
83
+ const newValue = inputEl.value;
84
+ const success = await this.updateEnvVar(varName, newValue);
85
+ if (success) {
86
+ this.editingVar = null;
87
+ }
88
+ }
89
+ }
90
+
91
+ handleCancelEdit() {
92
+ this.editingVar = null;
93
+ }
94
+
95
+ toggleAddForm() {
96
+ this.showAddForm = !this.showAddForm;
97
+ if (!this.showAddForm) {
98
+ this.newVarName = '';
99
+ this.newVarValue = '';
100
+ }
101
+ }
102
+
103
+ async handleAddVar() {
104
+ if (this.newVarName && this.newVarValue) {
105
+ const success = await this.updateEnvVar(this.newVarName, this.newVarValue);
106
+ if (success) {
107
+ this.newVarName = '';
108
+ this.newVarValue = '';
109
+ this.showAddForm = false;
110
+ }
111
+ }
112
+ }
113
+
114
+ handleSearchInput(e) {
115
+ this.searchTerm = e.target.value;
116
+ }
117
+
118
+ renderEnvironmentVars() {
119
+ if (!this.pluginData || !this.pluginData.current_env) {
120
+ return html`<div class="empty-state">No environment variables found</div>`;
121
+ }
122
+
123
+ const currentEnv = this.pluginData.current_env;
124
+
125
+ // Create a mapping of variable names to the plugins that reference them
126
+ const varsByPlugin = {};
127
+ const pluginsByVar = {};
128
+
129
+ for (const [pluginName, pluginInfo] of Object.entries(this.pluginData)) {
130
+ if (pluginName === 'current_env') continue;
131
+
132
+ for (const varName of pluginInfo.env_vars) {
133
+ if (!varsByPlugin[pluginName]) {
134
+ varsByPlugin[pluginName] = [];
135
+ }
136
+ varsByPlugin[pluginName].push(varName);
137
+
138
+ if (!pluginsByVar[varName]) {
139
+ pluginsByVar[varName] = [];
140
+ }
141
+ pluginsByVar[varName].push(pluginName);
142
+ }
143
+ }
144
+
145
+ // Group variables by plugin
146
+ const groupedByPlugin = {};
147
+ const multiPluginVars = [];
148
+
149
+ // First, identify variables referenced by multiple plugins
150
+ for (const [varName, plugins] of Object.entries(pluginsByVar)) {
151
+ if (plugins.length > 1) {
152
+ multiPluginVars.push({
153
+ varName,
154
+ varValue: currentEnv[varName],
155
+ plugins
156
+ });
157
+ } else {
158
+ // Single plugin variables
159
+ const plugin = plugins[0];
160
+ if (!groupedByPlugin[plugin]) {
161
+ groupedByPlugin[plugin] = [];
162
+ }
163
+ groupedByPlugin[plugin].push({
164
+ varName,
165
+ varValue: currentEnv[varName]
166
+ });
167
+ }
168
+ }
169
+
170
+ // Sort plugins alphabetically
171
+ const sortedPlugins = Object.keys(groupedByPlugin).sort();
172
+
173
+ // Sort multi-plugin variables by name
174
+ multiPluginVars.sort((a, b) => a.varName.localeCompare(b.varName));
175
+
176
+ // Apply search filter if needed
177
+ let filteredPlugins = sortedPlugins;
178
+ let filteredMultiVars = multiPluginVars;
179
+
180
+ if (this.searchTerm) {
181
+ const searchTerm = this.searchTerm.toLowerCase();
182
+
183
+ // Filter plugins and their variables
184
+ filteredPlugins = sortedPlugins.filter(plugin => {
185
+ // Check if plugin name matches
186
+ if (plugin.toLowerCase().includes(searchTerm)) return true;
187
+
188
+ // Check if any variable name matches
189
+ return groupedByPlugin[plugin].some(v =>
190
+ v.varName.toLowerCase().includes(searchTerm) ||
191
+ (v.varValue && v.varValue.toLowerCase().includes(searchTerm))
192
+ );
193
+ });
194
+
195
+ // For each plugin, filter its variables
196
+ for (const plugin of filteredPlugins) {
197
+ groupedByPlugin[plugin] = groupedByPlugin[plugin].filter(v =>
198
+ v.varName.toLowerCase().includes(searchTerm) ||
199
+ plugin.toLowerCase().includes(searchTerm) ||
200
+ (v.varValue && v.varValue.toLowerCase().includes(searchTerm))
201
+ );
202
+ }
203
+
204
+ // Filter multi-plugin variables
205
+ filteredMultiVars = multiPluginVars.filter(v =>
206
+ v.varName.toLowerCase().includes(searchTerm) ||
207
+ v.plugins.some(p => p.toLowerCase().includes(searchTerm)) ||
208
+ (v.varValue && v.varValue.toLowerCase().includes(searchTerm))
209
+ );
210
+ }
211
+
212
+ // Check if we have any results after filtering
213
+ const hasResults = filteredPlugins.some(p => groupedByPlugin[p].length > 0) || filteredMultiVars.length > 0;
214
+
215
+ if (!hasResults) {
216
+ return html`<div class="empty-state">No matching environment variables found</div>`;
217
+ }
218
+
219
+ return html`
220
+ <table class="env-table">
221
+ <thead>
222
+ <tr>
223
+ <th>Variable Name</th>
224
+ <th>Value</th>
225
+ <th>Actions</th>
226
+ </tr>
227
+ </thead>
228
+ <tbody>
229
+ ${filteredPlugins.map(plugin => {
230
+ if (groupedByPlugin[plugin].length === 0) return '';
231
+
232
+ return html`
233
+ <tr class="plugin-header">
234
+ <td colspan="3" class="plugin-name">${plugin}</td>
235
+ </tr>
236
+ ${groupedByPlugin[plugin].map(v => {
237
+ const { varName, varValue } = v;
238
+ const isMasked = varValue === '********';
239
+
240
+ return html`
241
+ <tr class="plugin-var">
242
+ <td><span class="var-name" title="${varName}">${varName}</span></td>
243
+ <td>
244
+ ${this.editingVar === varName ?
245
+ html`
246
+ <div class="edit-form">
247
+ <input
248
+ id="edit-${varName}"
249
+ type="text"
250
+ value="${isMasked ? '' : varValue}"
251
+ placeholder="${isMasked ? 'Enter new value' : ''}">
252
+ </div>
253
+ ` :
254
+ html`<span class="var-value ${isMasked ? 'masked' : ''}">${varValue}</span>`
255
+ }
256
+ </td>
257
+ <td>
258
+ <div class="actions">
259
+ ${this.editingVar === varName ?
260
+ html`
261
+ <button class="small primary" @click=${() => this.handleSaveEdit(varName)}>Save</button>
262
+ <button class="small" @click=${this.handleCancelEdit}>Cancel</button>
263
+ ` :
264
+ html`<button class="small" @click=${() => this.handleEditClick(varName)}>Edit</button>`
265
+ }
266
+ </div>
267
+ </td>
268
+ </tr>
269
+ `;
270
+ })}
271
+ `;
272
+ })}
273
+
274
+ ${filteredMultiVars.length > 0 ? html`
275
+ <tr class="plugin-header">
276
+ <td colspan="3" class="plugin-name">Multiple Plugins</td>
277
+ </tr>
278
+ ${filteredMultiVars.map(v => {
279
+ const { varName, varValue, plugins } = v;
280
+ const isMasked = varValue === '********';
281
+
282
+ return html`
283
+ <tr class="plugin-var">
284
+ <td>
285
+ <span class="var-name" title="${varName}">${varName}</span>
286
+ <div class="plugin-refs">
287
+ ${plugins.map(p => html`<span class="plugin-tag">${p}</span>`)}
288
+ </div>
289
+ </td>
290
+ <td>
291
+ ${this.editingVar === varName ?
292
+ html`
293
+ <div class="edit-form">
294
+ <input
295
+ id="edit-${varName}"
296
+ type="text"
297
+ value="${isMasked ? '' : varValue}"
298
+ placeholder="${isMasked ? 'Enter new value' : ''}">
299
+ </div>
300
+ ` :
301
+ html`<span class="var-value ${isMasked ? 'masked' : ''}">${varValue}</span>`
302
+ }
303
+ </td>
304
+ <td>
305
+ <div class="actions">
306
+ ${this.editingVar === varName ?
307
+ html`
308
+ <button class="small primary" @click=${() => this.handleSaveEdit(varName)}>Save</button>
309
+ <button class="small" @click=${this.handleCancelEdit}>Cancel</button>
310
+ ` :
311
+ html`<button class="small" @click=${() => this.handleEditClick(varName)}>Edit</button>`
312
+ }
313
+ </div>
314
+ </td>
315
+ </tr>
316
+ `;
317
+ })}
318
+ ` : ''}
319
+ </tbody>
320
+ </table>
321
+ `;
322
+ }
323
+
324
+ _render() {
325
+ return html`
326
+ <link rel="stylesheet" href="/env_manager/static/css/env-manager.css">
327
+ <div class="env-manager">
328
+ <div class="section">
329
+ <h3>
330
+ Environment Variables
331
+ <div class="controls">
332
+ <input
333
+ type="text"
334
+ class="search-box"
335
+ placeholder="Search..."
336
+ @input=${this.handleSearchInput}
337
+ .value=${this.searchTerm}>
338
+ <button @click=${this.toggleAddForm}>
339
+ ${this.showAddForm ? 'Cancel' : 'Add Variable'}
340
+ </button>
341
+ </div>
342
+ </h3>
343
+
344
+ ${this.loading ?
345
+ html`<div class="loading">Loading environment variables...</div>` :
346
+ html`
347
+ ${this.showAddForm ?
348
+ html`
349
+ <div class="add-form">
350
+ <div class="form-row">
351
+ <input
352
+ type="text"
353
+ placeholder="Variable Name"
354
+ .value=${this.newVarName}
355
+ @input=${e => this.newVarName = e.target.value}>
356
+ </div>
357
+ <div class="form-row">
358
+ <input
359
+ type="text"
360
+ placeholder="Variable Value"
361
+ .value=${this.newVarValue}
362
+ @input=${e => this.newVarValue = e.target.value}>
363
+ </div>
364
+ <div class="form-actions">
365
+ <button class="primary" @click=${this.handleAddVar}>Add Variable</button>
366
+ </div>
367
+ </div>
368
+ ` : ''
369
+ }
370
+
371
+ ${this.renderEnvironmentVars()}
372
+ `
373
+ }
374
+ </div>
375
+ </div>
376
+ `;
377
+ }
378
+ }
379
+
380
+ customElements.define('env-manager', EnvManager);
@@ -4,6 +4,9 @@ from fastapi.templating import Jinja2Templates
4
4
  from lib.templates import render
5
5
  from lib.route_decorators import add_public_static
6
6
  import os
7
+ import glob
8
+ from datetime import datetime
9
+ import time
7
10
 
8
11
  router = APIRouter()
9
12
 
@@ -14,8 +17,36 @@ add_public_static('/home/static/')
14
17
 
15
18
  @router.get("/", response_class=HTMLResponse)
16
19
  async def home(request: Request):
17
- agents = [agent for agent in os.listdir("data/agents/local") if os.path.isdir(os.path.join("data/agents/local", agent))]
20
+ # Get all agent directories
21
+ agent_dirs = [agent for agent in os.listdir("data/agents/local") if os.path.isdir(os.path.join("data/agents/local", agent))]
22
+
23
+ # Try to sort agents by last access time
24
+ agent_access_times = []
25
+ for agent in agent_dirs:
26
+ # Look for the most recent log file for this agent
27
+ log_pattern = f"data/chatlogs/{agent}_*.json"
28
+ log_files = glob.glob(log_pattern)
29
+
30
+ if log_files:
31
+ # Get the most recent log file's modification time
32
+ latest_log = max(log_files, key=os.path.getmtime)
33
+ mtime = os.path.getmtime(latest_log)
34
+ else:
35
+ # If no logs, use the agent.json file's modification time
36
+ agent_file = os.path.join("data/agents/local", agent, "agent.json")
37
+ if os.path.exists(agent_file):
38
+ mtime = os.path.getmtime(agent_file)
39
+ else:
40
+ mtime = 0 # Default to oldest if no file found
41
+
42
+ agent_access_times.append((agent, mtime))
43
+
44
+ # Sort by modification time (most recent first)
45
+ agent_access_times.sort(key=lambda x: x[1], reverse=True)
46
+
47
+ # Extract just the agent names in sorted order
48
+ agents = [agent for agent, _ in agent_access_times]
49
+
18
50
  user = request.state.user
19
51
  html = await render('home', {"user": user, "request": request, "agents": agents })
20
52
  return HTMLResponse(html)
21
-
@@ -1,7 +1,8 @@
1
- /* Enhanced styles for XinGen Agent Host */
1
+ /* Enhanced styles for MindRoot Agent Host */
2
2
 
3
3
  .agent-container {
4
- max-width: 1200px;
4
+ width: 90%; /* Use percentage instead of fixed width */
5
+ max-width: 2000px; /* Increased from 1200px for wider screens */
5
6
  margin: 0 auto;
6
7
  padding: 2rem;
7
8
  }
@@ -9,6 +10,7 @@
9
10
  .logo {
10
11
  transition: transform 0.3s ease;
11
12
  margin-bottom: 2rem;
13
+ max-width: 120px; /* Control logo size */
12
14
  }
13
15
 
14
16
  .logo:hover {
@@ -22,13 +24,15 @@ h1 {
22
24
  background: linear-gradient(45deg, #6e8efb, #a777e3);
23
25
  -webkit-background-clip: text;
24
26
  -webkit-text-fill-color: transparent;
27
+ text-align: center;
25
28
  }
26
29
 
27
30
  .agent-list {
28
31
  display: grid;
29
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
32
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
30
33
  gap: 1.5rem;
31
34
  margin-top: 2rem;
35
+ width: 100%;
32
36
  }
33
37
 
34
38
  .agent-link {
@@ -38,9 +42,12 @@ h1 {
38
42
  transition: all 0.3s ease;
39
43
  border: 1px solid rgba(255, 255, 255, 0.1);
40
44
  display: flex;
45
+ flex-direction: column;
41
46
  align-items: center;
47
+ text-align: center;
42
48
  text-decoration: none;
43
49
  color: inherit;
50
+ height: 100%;
44
51
  }
45
52
 
46
53
  .agent-link:hover {
@@ -54,13 +61,40 @@ h1 {
54
61
  height: 8px;
55
62
  border-radius: 50%;
56
63
  background: #4CAF50;
57
- margin-right: 12px;
64
+ margin-bottom: 8px;
65
+ }
66
+
67
+ .agent-avatar {
68
+ width: 160px;
69
+ height: 160px;
70
+ border-radius: 50%;
71
+ object-fit: cover;
72
+ margin-bottom: 16px;
73
+ border: 2px solid rgba(255, 255, 255, 0.2);
74
+ transition: all 0.3s ease;
75
+ }
76
+
77
+ .agent-link:hover .agent-avatar {
78
+ border-color: rgba(255, 255, 255, 0.5);
79
+ transform: scale(1.05);
80
+ }
81
+
82
+ .agent-info {
83
+ display: flex;
84
+ flex-direction: column;
85
+ align-items: center;
86
+ width: 100%;
58
87
  }
59
88
 
60
89
  .agent-name {
61
90
  font-size: 1.2rem;
62
91
  font-weight: 500;
63
92
  letter-spacing: 0.5px;
93
+ word-break: keep-all;
94
+ max-width: 100%;
95
+ overflow-wrap: break-word;
96
+ line-height: 1.3;
97
+ white-space: normal;
64
98
  }
65
99
 
66
100
  .admin-link {
@@ -79,7 +113,7 @@ h1 {
79
113
  }
80
114
 
81
115
  .admin-link:visited {
82
- color: #ddd;
116
+ color: #ddd;
83
117
  }
84
118
 
85
119
  h2 {
@@ -87,4 +121,76 @@ h2 {
87
121
  font-weight: 500;
88
122
  margin-bottom: 1.5rem;
89
123
  color: rgba(255, 255, 255, 0.9);
124
+ text-align: center;
125
+ }
126
+
127
+ /* Responsive breakpoints */
128
+
129
+ /* Extra large screens */
130
+ @media (min-width: 1800px) {
131
+ .agent-list {
132
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
133
+ }
134
+
135
+ .agent-container {
136
+ width: 85%;
137
+ max-width: 2400px;
138
+ }
139
+ }
140
+
141
+ /* Large screens */
142
+ @media (min-width: 1200px) and (max-width: 1799px) {
143
+ .agent-list {
144
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
145
+ }
146
+
147
+ .agent-container {
148
+ width: 90%;
149
+ }
150
+ }
151
+
152
+ /* Medium screens */
153
+ @media (min-width: 768px) and (max-width: 1199px) {
154
+ .agent-list {
155
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
156
+ }
157
+
158
+ .agent-avatar {
159
+ width: 140px;
160
+ height: 140px;
161
+ }
162
+
163
+ .agent-container {
164
+ width: 95%;
165
+ }
166
+ }
167
+
168
+ /* Small screens */
169
+ @media (max-width: 767px) {
170
+ .agent-list {
171
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
172
+ gap: 1rem;
173
+ }
174
+
175
+ .agent-avatar {
176
+ width: 120px;
177
+ height: 120px;
178
+ }
179
+
180
+ .agent-name {
181
+ font-size: 1rem;
182
+ }
183
+
184
+ .agent-container {
185
+ width: 100%;
186
+ padding: 1rem;
187
+ }
188
+
189
+ h1 {
190
+ font-size: 2rem;
191
+ }
192
+
193
+ h2 {
194
+ font-size: 1.3rem;
195
+ }
90
196
  }
@@ -10,6 +10,7 @@
10
10
  {% block head_title %}
11
11
  <title>MindRoot Agent Host</title>
12
12
  {% endblock %}
13
+
13
14
  {% block head_css %}
14
15
  <link rel="stylesheet" href="/home/static/css/reset.css">
15
16
  <link rel="stylesheet" href="/home/static/css/default.css">
@@ -51,9 +52,12 @@
51
52
 
52
53
  <div class="agent-list" role="list">
53
54
  {% for agent in agents %}
54
- <a href="/agent/{{ agent }}" class="agent-link" role="listitem" target="_blank" >
55
- <span class="agent-status" aria-hidden="true"></span>
56
- <span class="agent-name">{{ agent }}</span>
55
+ <a href="/agent/{{ agent }}" class="agent-link" role="listitem" target="_blank">
56
+ <img src="/chat/personas/{{ agent }}/avatar.png" alt="{{ agent }} avatar" class="agent-avatar" onerror="this.src='/chat/static/assistant.png'">
57
+ <div class="agent-info">
58
+ <span class="agent-status" aria-hidden="true"></span>
59
+ <span class="agent-name">{{ agent | replace('_', ' ') }}</span>
60
+ </div>
57
61
  </a>
58
62
  {% endfor %}
59
63
  </div>
@@ -74,4 +78,3 @@
74
78
  {% endblock %}
75
79
  </body>
76
80
  </html>
77
-
mindroot/lib/chatlog.py CHANGED
@@ -70,7 +70,11 @@ class ChatLog:
70
70
  except Exception as e:
71
71
  # assume previous mesage was not a command, was a string
72
72
  debug_box("4")
73
- new_msg_text = self.messages[-1]['content'][0]['text'] + message['content'][0]['text']
73
+ print("Could not combine commands, probably normal if user message and previous system output, assuming string", e)
74
+ if type(self.messages[-1]['content']) == str:
75
+ new_msg_text = self.messages[-1]['content'] + message['content'][0]['text']
76
+ else:
77
+ new_msg_text = self.messages[-1]['content'][0]['text'] + message['content'][0]['text']
74
78
  self.messages.append({'role': message['role'], 'content': [{'type': 'text', 'text': new_msg_text}]})
75
79
  #print('could not combine commands. probably normal if user message and previous system output', e)
76
80
  #print(self.messages[-1])