mindroot 10.4.0__py3-none-any.whl → 10.6.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.
- mindroot/coreplugins/admin/static/js/agent-form.js +26 -1
- mindroot/coreplugins/admin/static/js/persona-editor.js +14 -1
- mindroot/coreplugins/agent/agent.py +17 -1
- mindroot/coreplugins/chat/edit_router.py +495 -0
- mindroot/coreplugins/chat/services.py +20 -1
- mindroot/coreplugins/chat/templates/chat.jinja2 +4 -0
- mindroot/coreplugins/chat/widget_routes.py +1 -1
- mindroot/lib/chatlog.py +8 -0
- {mindroot-10.4.0.dist-info → mindroot-10.6.0.dist-info}/METADATA +1 -1
- {mindroot-10.4.0.dist-info → mindroot-10.6.0.dist-info}/RECORD +14 -13
- {mindroot-10.4.0.dist-info → mindroot-10.6.0.dist-info}/WHEEL +0 -0
- {mindroot-10.4.0.dist-info → mindroot-10.6.0.dist-info}/entry_points.txt +0 -0
- {mindroot-10.4.0.dist-info → mindroot-10.6.0.dist-info}/licenses/LICENSE +0 -0
- {mindroot-10.4.0.dist-info → mindroot-10.6.0.dist-info}/top_level.txt +0 -0
|
@@ -658,7 +658,14 @@ class AgentForm extends BaseEl {
|
|
|
658
658
|
}
|
|
659
659
|
console.log('before',this.agent)
|
|
660
660
|
// Handle all other inputs
|
|
661
|
-
|
|
661
|
+
let inputValue;
|
|
662
|
+
if (type === 'checkbox') {
|
|
663
|
+
inputValue = checked;
|
|
664
|
+
} else if (type === 'number') {
|
|
665
|
+
inputValue = value === '' ? null : Number(value);
|
|
666
|
+
} else {
|
|
667
|
+
inputValue = value;
|
|
668
|
+
}
|
|
662
669
|
this.agent = { ...this.agent, [name]: inputValue };
|
|
663
670
|
console.log('after', this.agent)
|
|
664
671
|
}
|
|
@@ -1273,6 +1280,24 @@ class AgentForm extends BaseEl {
|
|
|
1273
1280
|
</details>
|
|
1274
1281
|
</div>
|
|
1275
1282
|
|
|
1283
|
+
<div class="form-group commands-section">
|
|
1284
|
+
<details>
|
|
1285
|
+
<summary>Max Tokens</summary>
|
|
1286
|
+
<div class="commands-category">
|
|
1287
|
+
<div class="form-group">
|
|
1288
|
+
<label>Maximum Tokens:</label>
|
|
1289
|
+
<input
|
|
1290
|
+
type="number"
|
|
1291
|
+
name="max_tokens"
|
|
1292
|
+
.value=${agentForRender.max_tokens || ''}
|
|
1293
|
+
placeholder="Leave empty for model default"
|
|
1294
|
+
@input=${this.handleInputChange}
|
|
1295
|
+
>
|
|
1296
|
+
</div>
|
|
1297
|
+
</div>
|
|
1298
|
+
</details>
|
|
1299
|
+
</div>
|
|
1300
|
+
|
|
1276
1301
|
<div class="form-group commands-section">
|
|
1277
1302
|
<details>
|
|
1278
1303
|
<summary>Recommended Plugins</summary>
|
|
@@ -10,7 +10,8 @@ class PersonaEditor extends BaseEl {
|
|
|
10
10
|
personas: { type: Array, reflect: true },
|
|
11
11
|
newPersona: { type: Boolean, reflect: true },
|
|
12
12
|
facerefFileName: { type: String, reflect: true },
|
|
13
|
-
avatarFileName: { type: String, reflect: true }
|
|
13
|
+
avatarFileName: { type: String, reflect: true },
|
|
14
|
+
voiceId: { type: String, reflect: true }
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
static styles = [
|
|
@@ -130,6 +131,7 @@ class PersonaEditor extends BaseEl {
|
|
|
130
131
|
this.newPersona = false;
|
|
131
132
|
this.facerefFileName = '';
|
|
132
133
|
this.avatarFileName = '';
|
|
134
|
+
this.voiceId = '';
|
|
133
135
|
this.fetchPersonas();
|
|
134
136
|
}
|
|
135
137
|
|
|
@@ -168,13 +170,16 @@ class PersonaEditor extends BaseEl {
|
|
|
168
170
|
if (this.scope === 'registry') {
|
|
169
171
|
// For registry personas, use the full path format
|
|
170
172
|
const response = await fetch(`/personas/registry/${this.name}`);
|
|
173
|
+
this.voiceId = this.persona.voice_id || '';
|
|
171
174
|
this.persona = await response.json();
|
|
172
175
|
} else {
|
|
173
176
|
const response = await fetch(`/personas/${this.scope}/${this.name}`);
|
|
174
177
|
this.persona = await response.json();
|
|
178
|
+
this.voiceId = this.persona.voice_id || '';
|
|
175
179
|
}
|
|
176
180
|
} else {
|
|
177
181
|
this.persona = {};
|
|
182
|
+
this.voiceId = '';
|
|
178
183
|
}
|
|
179
184
|
}
|
|
180
185
|
|
|
@@ -194,11 +199,15 @@ class PersonaEditor extends BaseEl {
|
|
|
194
199
|
this.persona = {};
|
|
195
200
|
this.facerefFileName = '';
|
|
196
201
|
this.avatarFileName = '';
|
|
202
|
+
this.voiceId = '';
|
|
197
203
|
}
|
|
198
204
|
|
|
199
205
|
handleInputChange(event) {
|
|
200
206
|
const { name, value, type, checked } = event.target;
|
|
201
207
|
const inputValue = type === 'checkbox' ? checked : value;
|
|
208
|
+
if (name === 'voice_id') {
|
|
209
|
+
this.voiceId = value;
|
|
210
|
+
}
|
|
202
211
|
this.persona = { ...this.persona, [name]: inputValue };
|
|
203
212
|
}
|
|
204
213
|
|
|
@@ -280,6 +289,10 @@ class PersonaEditor extends BaseEl {
|
|
|
280
289
|
<label>Negative Appearance:</label>
|
|
281
290
|
<textarea class="text_lg" name="negative_appearance" .value=${this.persona.negative_appearance || ''} @input=${this.handleInputChange}></textarea>
|
|
282
291
|
</div>
|
|
292
|
+
<div class="form-group">
|
|
293
|
+
<label>Voice ID:</label>
|
|
294
|
+
<input class="text_inp" type="text" name="voice_id" .value=${this.voiceId || ''} @input=${this.handleInputChange} placeholder="Optional voice identifier for TTS" />
|
|
295
|
+
</div>
|
|
283
296
|
<div class="form-group">
|
|
284
297
|
<label>
|
|
285
298
|
Moderated:
|
|
@@ -340,7 +340,8 @@ class Agent:
|
|
|
340
340
|
logger.debug(f"Processing command: {cmd}")
|
|
341
341
|
await context.partial_command(cmd_name, json.dumps(cmd_args), cmd_args)
|
|
342
342
|
|
|
343
|
-
|
|
343
|
+
self.handle_cmds(cmd_name, cmd_args, json_cmd=json.dumps(cmd), context=context)
|
|
344
|
+
|
|
344
345
|
cmd_task = asyncio.create_task(
|
|
345
346
|
self.handle_cmds(cmd_name, cmd_args, json_cmd=json.dumps(cmd), context=context)
|
|
346
347
|
)
|
|
@@ -355,6 +356,14 @@ class Agent:
|
|
|
355
356
|
await context.command_result(cmd_name, result)
|
|
356
357
|
sys_header = "Note: tool command results follow, not user replies"
|
|
357
358
|
sys_header = ""
|
|
359
|
+
|
|
360
|
+
if result == "SYSTEM: WARNING - Command interrupted!\n\n":
|
|
361
|
+
logger.warning("Command was interrupted. Skipping any extra commands in list.")
|
|
362
|
+
await context.chat_log.drop_last('assistant')
|
|
363
|
+
return results, full_cmds
|
|
364
|
+
break
|
|
365
|
+
|
|
366
|
+
|
|
358
367
|
full_cmds.append({ "SYSTEM": sys_header, "cmd": cmd_name, "args": cmd_args, "result": result})
|
|
359
368
|
if result is not None:
|
|
360
369
|
results.append({"SYSTEM": sys_header, "cmd": cmd_name, "args": { "omitted": "(see command msg.)"}, "result": result})
|
|
@@ -473,6 +482,7 @@ class Agent:
|
|
|
473
482
|
tmp_data = { "messages": new_messages }
|
|
474
483
|
debug_box("Filtering messages")
|
|
475
484
|
#debug_box(tmp_data)
|
|
485
|
+
|
|
476
486
|
tmp_data = await pipeline_manager.filter_messages(tmp_data, context=context)
|
|
477
487
|
new_messages = tmp_data['messages']
|
|
478
488
|
except Exception as e:
|
|
@@ -487,6 +497,12 @@ class Agent:
|
|
|
487
497
|
if not isinstance(context.agent, dict):
|
|
488
498
|
context.agent = await get_agent_data(context.agent, context=context)
|
|
489
499
|
|
|
500
|
+
if 'max_tokens' in context.agent and context.agent['max_tokens'] is not None and context.agent['max_tokens'] != '':
|
|
501
|
+
logger.info(f"Using agent max tokens {max_tokens}")
|
|
502
|
+
max_tokens = context.agent['max_tokens']
|
|
503
|
+
else:
|
|
504
|
+
logger.info(f"Using default max tokens {max_tokens}")
|
|
505
|
+
|
|
490
506
|
if model is None:
|
|
491
507
|
if 'service_models' in context.agent and context.agent['service_models'] is not None:
|
|
492
508
|
if context.agent['service_models'].get('stream_chat', None) is None:
|
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
from fastapi import APIRouter, HTTPException, Request, Response, Depends, Query
|
|
2
|
+
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse
|
|
3
|
+
from fastapi import File, UploadFile, Form
|
|
4
|
+
from sse_starlette.sse import EventSourceResponse
|
|
5
|
+
from .models import MessageParts
|
|
6
|
+
from lib.providers.services import service, service_manager
|
|
7
|
+
from .services import init_chat_session, send_message_to_agent, subscribe_to_agent_messages, get_chat_history, run_task
|
|
8
|
+
from lib.templates import render
|
|
9
|
+
from lib.auth.auth import require_user
|
|
10
|
+
from lib.plugins import list_enabled
|
|
11
|
+
import nanoid
|
|
12
|
+
from lib.providers.commands import *
|
|
13
|
+
import asyncio
|
|
14
|
+
from lib.chatcontext import get_context, ChatContext
|
|
15
|
+
from typing import List
|
|
16
|
+
from lib.providers.services import service, service_manager
|
|
17
|
+
from lib.providers.commands import command_manager
|
|
18
|
+
from lib.utils.debug import debug_box
|
|
19
|
+
from lib.session_files import load_session_data, save_session_data
|
|
20
|
+
import os
|
|
21
|
+
import json
|
|
22
|
+
from lib.chatcontext import ChatContext
|
|
23
|
+
import shutil
|
|
24
|
+
from pydantic import BaseModel
|
|
25
|
+
from lib.auth.api_key import verify_api_key
|
|
26
|
+
from .services import active_send_tasks, cancel_active_send_task, get_active_send_tasks, cleanup_completed_tasks
|
|
27
|
+
|
|
28
|
+
router = APIRouter()
|
|
29
|
+
|
|
30
|
+
# Global dictionary to store tasks
|
|
31
|
+
tasks = {}
|
|
32
|
+
|
|
33
|
+
@router.post("/chat/{log_id}/{task_id}/cancel")
|
|
34
|
+
async def cancel_chat(request: Request, log_id: str, task_id: str):
|
|
35
|
+
debug_box("cancel_chat")
|
|
36
|
+
print("Trying to cancel task", task_id)
|
|
37
|
+
user = request.state.user.username
|
|
38
|
+
context = await get_context(log_id, user)
|
|
39
|
+
debug_box(str(context))
|
|
40
|
+
|
|
41
|
+
# Cancel the active send_message_to_agent task if it exists
|
|
42
|
+
if log_id in active_send_tasks:
|
|
43
|
+
task_info = active_send_tasks[log_id]
|
|
44
|
+
task = task_info['task']
|
|
45
|
+
|
|
46
|
+
if not task.done():
|
|
47
|
+
print(f"Cancelling active send_message_to_agent task for session {log_id}")
|
|
48
|
+
task.cancel()
|
|
49
|
+
context.data['task_cancelled'] = True
|
|
50
|
+
context.data['cancel_current_turn'] = True
|
|
51
|
+
|
|
52
|
+
# Also cancel the router task if it exists
|
|
53
|
+
if task_id in tasks:
|
|
54
|
+
tasks[task_id].cancel()
|
|
55
|
+
del tasks[task_id]
|
|
56
|
+
|
|
57
|
+
return {"status": "ok", "message": "Task cancelled successfully"}
|
|
58
|
+
else:
|
|
59
|
+
return {"status": "error", "message": "No active task found"}
|
|
60
|
+
else:
|
|
61
|
+
return {"status": "error", "message": "No active task found for this session"}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@router.get("/context1/{log_id}")
|
|
65
|
+
async def context1(request: Request, log_id: str):
|
|
66
|
+
user = request.state.user.username
|
|
67
|
+
context = await get_context(log_id, user)
|
|
68
|
+
print(context)
|
|
69
|
+
return "ok"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# need to serve persona images from ./personas/local/[persona_path]/avatar.png
|
|
73
|
+
@router.get("/chat/personas/{persona_path:path}/avatar.png")
|
|
74
|
+
async def get_persona_avatar(persona_path: str):
|
|
75
|
+
# Check if this is a registry persona with deduplicated assets
|
|
76
|
+
if persona_path.startswith("registry/"):
|
|
77
|
+
persona_json_path = f"personas/{persona_path}/persona.json"
|
|
78
|
+
if os.path.exists(persona_json_path):
|
|
79
|
+
try:
|
|
80
|
+
with open(persona_json_path, "r") as f:
|
|
81
|
+
persona_data = json.load(f)
|
|
82
|
+
|
|
83
|
+
# Check if persona has asset hashes (deduplicated storage)
|
|
84
|
+
asset_hashes = persona_data.get("asset_hashes", {})
|
|
85
|
+
if "avatar" in asset_hashes:
|
|
86
|
+
# Redirect to deduplicated asset endpoint
|
|
87
|
+
return RedirectResponse(f"/assets/{asset_hashes['avatar']}")
|
|
88
|
+
except Exception as e:
|
|
89
|
+
print(f"Error checking for deduplicated assets: {e}")
|
|
90
|
+
|
|
91
|
+
# Handle registry personas: registry/owner/name
|
|
92
|
+
if persona_path.startswith('registry/'):
|
|
93
|
+
file_path = f"personas/{persona_path}/avatar.png"
|
|
94
|
+
else:
|
|
95
|
+
# Legacy support: check local first, then shared
|
|
96
|
+
file_path = f"personas/local/{persona_path}/avatar.png"
|
|
97
|
+
if not os.path.exists(file_path):
|
|
98
|
+
file_path = f"personas/registry/{persona_path}/avatar.png"
|
|
99
|
+
|
|
100
|
+
if not os.path.exists(file_path):
|
|
101
|
+
resolved = os.path.realpath(file_path)
|
|
102
|
+
return {"error": "File not found: " + resolved}
|
|
103
|
+
|
|
104
|
+
with open(file_path, "rb") as f:
|
|
105
|
+
image_bytes = f.read()
|
|
106
|
+
|
|
107
|
+
return Response(
|
|
108
|
+
content=image_bytes,
|
|
109
|
+
media_type="image/png",
|
|
110
|
+
headers={
|
|
111
|
+
"Cache-Control": "max-age=3600",
|
|
112
|
+
"Content-Disposition": "inline; filename=avatar.png"
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
@router.get("/chat/personas/{persona_path:path}/faceref.png")
|
|
117
|
+
async def get_persona_faceref(persona_path: str):
|
|
118
|
+
# Check if this is a registry persona with deduplicated assets
|
|
119
|
+
if persona_path.startswith("registry/"):
|
|
120
|
+
persona_json_path = f"personas/{persona_path}/persona.json"
|
|
121
|
+
if os.path.exists(persona_json_path):
|
|
122
|
+
try:
|
|
123
|
+
with open(persona_json_path, "r") as f:
|
|
124
|
+
persona_data = json.load(f)
|
|
125
|
+
|
|
126
|
+
# Check if persona has asset hashes (deduplicated storage)
|
|
127
|
+
asset_hashes = persona_data.get("asset_hashes", {})
|
|
128
|
+
if "faceref" in asset_hashes:
|
|
129
|
+
# Redirect to deduplicated asset endpoint
|
|
130
|
+
return RedirectResponse(f"/assets/{asset_hashes['faceref']}")
|
|
131
|
+
except Exception as e:
|
|
132
|
+
print(f"Error checking for deduplicated assets: {e}")
|
|
133
|
+
|
|
134
|
+
# Handle registry personas: registry/owner/name
|
|
135
|
+
if persona_path.startswith('registry/'):
|
|
136
|
+
file_path = f"personas/{persona_path}/faceref.png"
|
|
137
|
+
else:
|
|
138
|
+
# Legacy support: check local first, then shared
|
|
139
|
+
file_path = f"personas/local/{persona_path}/faceref.png"
|
|
140
|
+
if not os.path.exists(file_path):
|
|
141
|
+
file_path = f"personas/registry/{persona_path}/faceref.png"
|
|
142
|
+
|
|
143
|
+
if not os.path.exists(file_path):
|
|
144
|
+
# Fallback to avatar if faceref doesn't exist
|
|
145
|
+
return RedirectResponse(f"/chat/personas/{persona_path}/avatar.png")
|
|
146
|
+
|
|
147
|
+
with open(file_path, "rb") as f:
|
|
148
|
+
image_bytes = f.read()
|
|
149
|
+
|
|
150
|
+
return Response(
|
|
151
|
+
content=image_bytes,
|
|
152
|
+
media_type="image/png",
|
|
153
|
+
headers={
|
|
154
|
+
"Cache-Control": "max-age=3600",
|
|
155
|
+
"Content-Disposition": "inline; filename=faceref.png"
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
@router.get("/chat/{log_id}/events")
|
|
160
|
+
async def chat_events(log_id: str):
|
|
161
|
+
return EventSourceResponse(await subscribe_to_agent_messages(log_id))
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@router.post("/chat/{log_id}/send")
|
|
165
|
+
async def send_message(request: Request, log_id: str, message_parts: List[MessageParts] ):
|
|
166
|
+
user = request.state.user
|
|
167
|
+
debug_box("send_message")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
context = await get_context(log_id, user.username)
|
|
171
|
+
debug_box(str(context))
|
|
172
|
+
#context = ChatContext(command_manager, service_manager, user=user.user)
|
|
173
|
+
task = asyncio.create_task(send_message_to_agent(log_id, message_parts, context=context, user=user))
|
|
174
|
+
#task = asyncio.create_task(send_message_to_agent(log_id, message_parts, user=user))
|
|
175
|
+
|
|
176
|
+
task_id = nanoid.generate()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# Check if there's already an active task for this session and cancel it
|
|
180
|
+
if log_id in active_send_tasks:
|
|
181
|
+
existing_task_info = active_send_tasks[log_id]
|
|
182
|
+
existing_task = existing_task_info['task']
|
|
183
|
+
if not existing_task.done():
|
|
184
|
+
print(f"Cancelling existing task for session {log_id} before starting new one")
|
|
185
|
+
existing_task.cancel()
|
|
186
|
+
|
|
187
|
+
tasks[task_id] = task
|
|
188
|
+
|
|
189
|
+
return {"status": "ok", "task_id": task_id}
|
|
190
|
+
|
|
191
|
+
@router.get("/chat/{log_id}/active_tasks")
|
|
192
|
+
async def get_active_tasks(request: Request, log_id: str):
|
|
193
|
+
"""
|
|
194
|
+
Get information about active tasks for a session (for debugging)
|
|
195
|
+
"""
|
|
196
|
+
user = request.state.user.username
|
|
197
|
+
|
|
198
|
+
active_info = {}
|
|
199
|
+
if log_id in active_send_tasks:
|
|
200
|
+
task_info = active_send_tasks[log_id]
|
|
201
|
+
task = task_info['task']
|
|
202
|
+
active_info = {
|
|
203
|
+
"session_id": log_id,
|
|
204
|
+
"task_active": not task.done(),
|
|
205
|
+
"task_cancelled": task.cancelled() if task else False,
|
|
206
|
+
"created_at": task_info.get('created_at'),
|
|
207
|
+
"router_tasks": [tid for tid, t in tasks.items() if not t.done()]
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return {"status": "ok", "active_tasks": active_info}
|
|
211
|
+
|
|
212
|
+
@router.post("/chat/{log_id}/cancel_send_task")
|
|
213
|
+
async def cancel_send_task_endpoint(request: Request, log_id: str):
|
|
214
|
+
"""
|
|
215
|
+
Cancel the active send_message_to_agent task for a session.
|
|
216
|
+
"""
|
|
217
|
+
user = request.state.user.username
|
|
218
|
+
context = await get_context(log_id, user)
|
|
219
|
+
|
|
220
|
+
result = await cancel_active_send_task(log_id, context=context)
|
|
221
|
+
return result
|
|
222
|
+
|
|
223
|
+
@router.get("/chat/active_send_tasks")
|
|
224
|
+
async def get_all_active_send_tasks(request: Request):
|
|
225
|
+
"""
|
|
226
|
+
Get information about all active send_message_to_agent tasks.
|
|
227
|
+
"""
|
|
228
|
+
result = await get_active_send_tasks()
|
|
229
|
+
return result
|
|
230
|
+
|
|
231
|
+
@router.post("/chat/cleanup_completed_tasks")
|
|
232
|
+
async def cleanup_completed_tasks_endpoint(request: Request):
|
|
233
|
+
"""
|
|
234
|
+
Clean up completed tasks from memory.
|
|
235
|
+
"""
|
|
236
|
+
result = await cleanup_completed_tasks()
|
|
237
|
+
return result
|
|
238
|
+
|
|
239
|
+
@router.get("/agent/{agent_name}", response_class=HTMLResponse)
|
|
240
|
+
async def get_chat_html(request: Request, agent_name: str, api_key: str = Query(None), embed: bool = Query(False)):
|
|
241
|
+
# Handle API key authentication if provided
|
|
242
|
+
if api_key:
|
|
243
|
+
try:
|
|
244
|
+
user_data = await verify_api_key(api_key)
|
|
245
|
+
if not user_data:
|
|
246
|
+
raise HTTPException(status_code=401, detail="Invalid API key")
|
|
247
|
+
# Create a mock user object for API key users
|
|
248
|
+
class MockUser:
|
|
249
|
+
def __init__(self, username):
|
|
250
|
+
self.username = username
|
|
251
|
+
user = MockUser(user_data['username'])
|
|
252
|
+
except Exception as e:
|
|
253
|
+
raise HTTPException(status_code=401, detail="Invalid API key")
|
|
254
|
+
else:
|
|
255
|
+
# Use regular authentication
|
|
256
|
+
if not hasattr(request.state, "user"):
|
|
257
|
+
return RedirectResponse("/login")
|
|
258
|
+
user = request.state.user
|
|
259
|
+
|
|
260
|
+
log_id = nanoid.generate()
|
|
261
|
+
plugins = list_enabled()
|
|
262
|
+
print("Init chat with user", user)
|
|
263
|
+
print(f"Init chat with {agent_name}")
|
|
264
|
+
await init_chat_session(user, agent_name, log_id)
|
|
265
|
+
|
|
266
|
+
if hasattr(request.state, "access_token"):
|
|
267
|
+
debug_box("Access token found in request state, saving to session file")
|
|
268
|
+
access_token = request.state.access_token
|
|
269
|
+
await save_session_data(log_id, "access_token", access_token)
|
|
270
|
+
print("..")
|
|
271
|
+
debug_box("Access token saved to session file")
|
|
272
|
+
else:
|
|
273
|
+
debug_box("No access token found in request state")
|
|
274
|
+
|
|
275
|
+
# If embed mode is requested, redirect to embed session
|
|
276
|
+
if embed:
|
|
277
|
+
return RedirectResponse(f"/session/{agent_name}/{log_id}?embed=true")
|
|
278
|
+
|
|
279
|
+
# Regular redirect
|
|
280
|
+
return RedirectResponse(f"/session/{agent_name}/{log_id}")
|
|
281
|
+
|
|
282
|
+
@router.get("/makesession/{agent_name}")
|
|
283
|
+
async def make_session(request: Request, agent_name: str):
|
|
284
|
+
"""
|
|
285
|
+
Create a new chat session for the specified agent.
|
|
286
|
+
Returns a redirect to the chat session page.
|
|
287
|
+
"""
|
|
288
|
+
if not hasattr(request.state, "user"):
|
|
289
|
+
return RedirectResponse("/login")
|
|
290
|
+
user = request.state.user
|
|
291
|
+
log_id = nanoid.generate()
|
|
292
|
+
|
|
293
|
+
await init_chat_session(user, agent_name, log_id)
|
|
294
|
+
return JSONResponse({ "log_id": log_id })
|
|
295
|
+
|
|
296
|
+
@router.get("/history/{agent_name}/{log_id}")
|
|
297
|
+
async def chat_history(request: Request, agent_name: str, log_id: str):
|
|
298
|
+
user = request.state.user.username
|
|
299
|
+
history = await get_chat_history(agent_name, log_id, user)
|
|
300
|
+
if history is None or len(history) == 0:
|
|
301
|
+
try:
|
|
302
|
+
print("trying to load from system session")
|
|
303
|
+
history = await get_chat_history(agent_name, log_id, "system")
|
|
304
|
+
except Exception as e:
|
|
305
|
+
print("Error loading from system session:", e)
|
|
306
|
+
history = []
|
|
307
|
+
pass
|
|
308
|
+
return history
|
|
309
|
+
|
|
310
|
+
@router.get("/session/{agent_name}/{log_id}")
|
|
311
|
+
async def chat_session(request: Request, agent_name: str, log_id: str, embed: bool = Query(False)):
|
|
312
|
+
# Check authentication (API key or regular user)
|
|
313
|
+
plugins = list_enabled()
|
|
314
|
+
if not hasattr(request.state, "user"):
|
|
315
|
+
return RedirectResponse("/login")
|
|
316
|
+
|
|
317
|
+
user = request.state.user
|
|
318
|
+
agent = await service_manager.get_agent_data(agent_name)
|
|
319
|
+
persona = agent['persona']['name']
|
|
320
|
+
print("persona is:", persona)
|
|
321
|
+
auth_token = None
|
|
322
|
+
try:
|
|
323
|
+
auth_token = await load_session_data(log_id, "access_token")
|
|
324
|
+
except:
|
|
325
|
+
pass
|
|
326
|
+
chat_data = {"log_id": log_id, "agent_name": agent_name, "user": user, "persona": persona }
|
|
327
|
+
|
|
328
|
+
if auth_token is not None:
|
|
329
|
+
chat_data["access_token"] = auth_token
|
|
330
|
+
|
|
331
|
+
# Add embed mode flag
|
|
332
|
+
if embed:
|
|
333
|
+
chat_data["embed_mode"] = True
|
|
334
|
+
|
|
335
|
+
html = await render('chat', chat_data)
|
|
336
|
+
return HTMLResponse(html)
|
|
337
|
+
|
|
338
|
+
# use starlette staticfiles to mount ./imgs
|
|
339
|
+
app.mount("/published", StaticFiles(directory=str(published_dir)), name="published_indices")
|
|
340
|
+
|
|
341
|
+
class TaskRequest(BaseModel):
|
|
342
|
+
instructions: str
|
|
343
|
+
|
|
344
|
+
@router.post("/task/{agent_name}")
|
|
345
|
+
async def run_task_route(request: Request, agent_name: str, task_request: TaskRequest = None):
|
|
346
|
+
"""
|
|
347
|
+
Run a task for an agent with the given instructions.
|
|
348
|
+
This endpoint allows programmatic interaction with agents without a full chat session.
|
|
349
|
+
|
|
350
|
+
Parameters:
|
|
351
|
+
- agent_name: The name of the agent to run the task
|
|
352
|
+
- instructions: The instructions/prompt to send to the agent
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
- JSON with results and log_id for tracking
|
|
356
|
+
"""
|
|
357
|
+
|
|
358
|
+
user = request.state.user.username
|
|
359
|
+
|
|
360
|
+
instructions = None
|
|
361
|
+
if task_request is not None:
|
|
362
|
+
instructions = task_request.instructions
|
|
363
|
+
|
|
364
|
+
if not instructions:
|
|
365
|
+
return {"status": "error", "message": "No instructions provided"}
|
|
366
|
+
|
|
367
|
+
task_result, full_results, log_id = await run_task(instructions=instructions, agent_name=agent_name, user=user)
|
|
368
|
+
print(task_result)
|
|
369
|
+
print(full_results)
|
|
370
|
+
print(log_id)
|
|
371
|
+
return {"status": "ok", "results": task_result, "full_results": full_results, "log_id": log_id}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
@router.post("/chat/{log_id}/upload")
|
|
375
|
+
async def upload_file(request: Request, log_id: str, file: UploadFile = File(...)):
|
|
376
|
+
"""
|
|
377
|
+
Upload a file and store it in a user-specific directory.
|
|
378
|
+
Returns the file path that can be used in messages.
|
|
379
|
+
"""
|
|
380
|
+
user = request.state.user.username
|
|
381
|
+
|
|
382
|
+
# Create user uploads directory if it doesn't exist
|
|
383
|
+
user_upload_dir = f"data/users/{user}/uploads/{log_id}"
|
|
384
|
+
os.makedirs(user_upload_dir, exist_ok=True)
|
|
385
|
+
|
|
386
|
+
# Generate a safe filename to prevent path traversal
|
|
387
|
+
filename = os.path.basename(file.filename)
|
|
388
|
+
file_path = os.path.join(user_upload_dir, filename)
|
|
389
|
+
|
|
390
|
+
# Save the file
|
|
391
|
+
with open(file_path, "wb") as buffer:
|
|
392
|
+
shutil.copyfileobj(file.file, buffer)
|
|
393
|
+
|
|
394
|
+
# Return the file information
|
|
395
|
+
return {
|
|
396
|
+
"status": "ok",
|
|
397
|
+
"filename": filename,
|
|
398
|
+
"path": file_path,
|
|
399
|
+
"mime_type": file.content_type
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
from lib.chatlog import count_tokens_for_log_id
|
|
404
|
+
|
|
405
|
+
@router.get("/chat/{log_id}/tokens")
|
|
406
|
+
async def get_token_count(request: Request, log_id: str):
|
|
407
|
+
"""
|
|
408
|
+
Get token counts for a chat log identified by log_id, including any delegated tasks.
|
|
409
|
+
|
|
410
|
+
Parameters:
|
|
411
|
+
- log_id: The log ID to count tokens for
|
|
412
|
+
|
|
413
|
+
Returns:
|
|
414
|
+
- JSON with token counts or error message if log not found
|
|
415
|
+
"""
|
|
416
|
+
token_counts = await count_tokens_for_log_id(log_id)
|
|
417
|
+
|
|
418
|
+
if token_counts is None:
|
|
419
|
+
return {"status": "error", "message": f"Chat log with ID {log_id} not found"}
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
return {"status": "ok", "token_counts": token_counts}
|
|
423
|
+
|
|
424
|
+
@router.get("/chat/del_session/{log_id}")
|
|
425
|
+
async def delete_chat_session(request: Request, log_id: str, user=Depends(require_user)):
|
|
426
|
+
"""
|
|
427
|
+
Delete a chat session by log_id, including chat logs, context files, and all child sessions.
|
|
428
|
+
|
|
429
|
+
Parameters:
|
|
430
|
+
- log_id: The log ID of the session to delete
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
- JSON with success status and message
|
|
434
|
+
"""
|
|
435
|
+
try:
|
|
436
|
+
# Try to determine the agent name from the context file first
|
|
437
|
+
agent_name = "unknown"
|
|
438
|
+
context_dir = os.environ.get('CHATCONTEXT_DIR', 'data/context')
|
|
439
|
+
context_file_path = f"{context_dir}/{user.username}/context_{log_id}.json"
|
|
440
|
+
|
|
441
|
+
if os.path.exists(context_file_path):
|
|
442
|
+
try:
|
|
443
|
+
with open(context_file_path, 'r') as f:
|
|
444
|
+
context_data = json.load(f)
|
|
445
|
+
agent_name = context_data.get('agent_name', 'unknown')
|
|
446
|
+
print(f"Found agent name '{agent_name}' from context file for log_id {log_id}")
|
|
447
|
+
except Exception as e:
|
|
448
|
+
print(f"Error reading context file {context_file_path}: {e}")
|
|
449
|
+
|
|
450
|
+
# If we still don't have the agent name, try to find the chatlog file
|
|
451
|
+
if agent_name == "unknown":
|
|
452
|
+
from lib.chatlog import find_chatlog_file
|
|
453
|
+
chatlog_path = find_chatlog_file(log_id)
|
|
454
|
+
if chatlog_path:
|
|
455
|
+
# Extract agent from path: data/chat/{user}/{agent}/chatlog_{log_id}.json
|
|
456
|
+
path_parts = chatlog_path.split(os.sep)
|
|
457
|
+
if len(path_parts) >= 3:
|
|
458
|
+
agent_name = path_parts[-2] # Agent is the second-to-last part
|
|
459
|
+
print(f"Found agent name '{agent_name}' from chatlog file path for log_id {log_id}")
|
|
460
|
+
|
|
461
|
+
await ChatContext.delete_session_by_id(log_id=log_id, user=user.username, agent=agent_name, cascade=True)
|
|
462
|
+
|
|
463
|
+
return {"status": "ok", "message": f"Chat session {log_id} deleted successfully"}
|
|
464
|
+
except Exception as e:
|
|
465
|
+
print(f"Error deleting chat session {log_id}: {e}")
|
|
466
|
+
raise HTTPException(status_code=500, detail=f"Error deleting chat session: {str(e)}")
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
@router.get("/chat/{log_id}/tokens")
|
|
470
|
+
async def get_token_count_alt(request: Request, log_id: str):
|
|
471
|
+
"""
|
|
472
|
+
Alternative token count endpoint using token_counter module.
|
|
473
|
+
|
|
474
|
+
Parameters:
|
|
475
|
+
- log_id: The log ID to count tokens for
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
- JSON with token counts or error message if log not found
|
|
479
|
+
"""
|
|
480
|
+
from lib.token_counter import count_tokens_for_log_id
|
|
481
|
+
|
|
482
|
+
token_counts = await count_tokens_for_log_id(log_id)
|
|
483
|
+
|
|
484
|
+
if token_counts is None:
|
|
485
|
+
return {"status": "error", "message": f"Chat log with ID {log_id} not found"}
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
return {"status": "ok", "token_counts": token_counts}
|
|
489
|
+
|
|
490
|
+
# Include widget routes
|
|
491
|
+
try:
|
|
492
|
+
from .widget_routes import router as widget_router
|
|
493
|
+
router.include_router(widget_router)
|
|
494
|
+
except ImportError as e:
|
|
495
|
+
print(f"Warning: Could not load widget routes: {e}")
|
|
@@ -217,8 +217,25 @@ def process_result(result, formatted_results):
|
|
|
217
217
|
|
|
218
218
|
return formatted_results
|
|
219
219
|
|
|
220
|
+
in_progress = {}
|
|
221
|
+
|
|
220
222
|
@service()
|
|
221
223
|
async def send_message_to_agent(session_id: str, message: str | List[MessageParts], max_iterations=35, context=None, user=None):
|
|
224
|
+
global in_progress
|
|
225
|
+
existing_session = in_progress.get(session_id, False)
|
|
226
|
+
print(existing_session)
|
|
227
|
+
|
|
228
|
+
if existing_session:
|
|
229
|
+
context_ = await get_context(session_id, user)
|
|
230
|
+
context.data['finished_conversation'] = True
|
|
231
|
+
await asyncio.sleep(0.4)
|
|
232
|
+
else:
|
|
233
|
+
print('starting')
|
|
234
|
+
|
|
235
|
+
print('ok')
|
|
236
|
+
in_progress[session_id] = True
|
|
237
|
+
|
|
238
|
+
print('b')
|
|
222
239
|
if os.environ.get("MR_MAX_ITERATIONS") is not None:
|
|
223
240
|
max_iterations = int(os.environ.get("MR_MAX_ITERATIONS"))
|
|
224
241
|
if not user:
|
|
@@ -399,10 +416,12 @@ async def send_message_to_agent(session_id: str, message: str | List[MessagePart
|
|
|
399
416
|
print("Exiting send_message_to_agent: ", session_id, message, max_iterations)
|
|
400
417
|
|
|
401
418
|
await context.finished_chat()
|
|
419
|
+
in_progress.pop(session_id, None)
|
|
402
420
|
return [results, full_results]
|
|
403
421
|
except Exception as e:
|
|
404
422
|
print("Error in send_message_to_agent: ", e)
|
|
405
423
|
print(traceback.format_exc())
|
|
424
|
+
in_progress.pop(session_id, None)
|
|
406
425
|
return []
|
|
407
426
|
|
|
408
427
|
@pipe(name='process_results', priority=5)
|
|
@@ -548,4 +567,4 @@ async def cancel_active_response(log_id: str, context=None):
|
|
|
548
567
|
await context.save_context()
|
|
549
568
|
|
|
550
569
|
print(f"Cancelled active response for session {log_id}")
|
|
551
|
-
return {"status": "cancelled", "log_id": log_id}
|
|
570
|
+
return {"status": "cancelled", "log_id": log_id}
|
|
@@ -241,7 +241,7 @@ async def get_embed_script(token: str):
|
|
|
241
241
|
iframe.style.cssText = "width: 100%; height: 100%; border: none; border-radius: 12px;";
|
|
242
242
|
}}
|
|
243
243
|
iframe.src = config.baseUrl + "/chat/widget/" + config.token + "/session";
|
|
244
|
-
iframe.allow
|
|
244
|
+
iframe.setAttribute('allow', 'microphone');
|
|
245
245
|
chatContainer.appendChild(iframe);
|
|
246
246
|
isLoaded = true;
|
|
247
247
|
}}
|
mindroot/lib/chatlog.py
CHANGED
|
@@ -133,6 +133,7 @@ class ChatLog:
|
|
|
133
133
|
debug_box("5")
|
|
134
134
|
self.messages.append(message)
|
|
135
135
|
self._save_log_sync()
|
|
136
|
+
|
|
136
137
|
async def add_message_async(self, message: Dict[str, str]) -> None:
|
|
137
138
|
"""Async version for new code that needs non-blocking operations"""
|
|
138
139
|
should_save = self._add_message_impl(message)
|
|
@@ -145,6 +146,13 @@ class ChatLog:
|
|
|
145
146
|
len(self.messages[-1]['content']) > 0 and
|
|
146
147
|
self.messages[-1]['content'][0].get('type') == 'image'):
|
|
147
148
|
await self.save_log()
|
|
149
|
+
|
|
150
|
+
async def drop_last(self, role) -> None:
|
|
151
|
+
if len(self.messages) == 0:
|
|
152
|
+
return
|
|
153
|
+
if self.messages[-1]['role'] == role:
|
|
154
|
+
self.messages = self.messages[:-1]
|
|
155
|
+
await self.save_log()
|
|
148
156
|
|
|
149
157
|
def get_history(self) -> List[Dict[str, str]]:
|
|
150
158
|
return self.messages
|
|
@@ -36,7 +36,7 @@ mindroot/coreplugins/admin/static/css/reset.css,sha256=pN9wuf7laZeIt-QjbxqJXDfu7
|
|
|
36
36
|
mindroot/coreplugins/admin/static/css/update.css,sha256=J1lchkEY6WST1LLoyPhXjjCZ4-oqMu7ooWjJNduRmMs,3008
|
|
37
37
|
mindroot/coreplugins/admin/static/js/about-info.js,sha256=CRyI5xGX3huycwseT5c80zsqQkElcdZsKMofyzyCimI,9230
|
|
38
38
|
mindroot/coreplugins/admin/static/js/agent-editor.js,sha256=ZCJBNQ-l4kJj-ZufYuzEg45ZpqxwliNmxuqUa2GNiqY,2825
|
|
39
|
-
mindroot/coreplugins/admin/static/js/agent-form.js,sha256=
|
|
39
|
+
mindroot/coreplugins/admin/static/js/agent-form.js,sha256=cFyIAS6dVxvzqJgJra-a5aFtS6zau0roveDSgpUF63A,41403
|
|
40
40
|
mindroot/coreplugins/admin/static/js/agent-list.js,sha256=86mqFyHspx9EnzJpgUDyeAgEq-jcqQ4v96CrgfUXoGM,2239
|
|
41
41
|
mindroot/coreplugins/admin/static/js/api-key-script.js,sha256=By80j-cwgxRT96MUBiZbVJuRvr6OAhd-GPmAUdZDZ4E,11199
|
|
42
42
|
mindroot/coreplugins/admin/static/js/base.js,sha256=xGCA6ngMapQ_jqMgHXg__CS3R46qprycOjkKTFDCMlA,1307
|
|
@@ -52,7 +52,7 @@ mindroot/coreplugins/admin/static/js/missing-commands.js,sha256=adNF9GWN981_KX7H
|
|
|
52
52
|
mindroot/coreplugins/admin/static/js/model-preferences-v2.js,sha256=4SFZBia959kRrSZXzIdWB0dscg4WtVIGYuLFlLpqBBI,15669
|
|
53
53
|
mindroot/coreplugins/admin/static/js/model-preferences.js,sha256=J0G7gcGACaPyslWJO42urf5wbZZsqO0LyPicAu-uV_Y,3365
|
|
54
54
|
mindroot/coreplugins/admin/static/js/notification.js,sha256=296rVCr6MNtzvzdzW3bGiMa231-BnWJtwZZ_sDWX-3c,5633
|
|
55
|
-
mindroot/coreplugins/admin/static/js/persona-editor.js,sha256=
|
|
55
|
+
mindroot/coreplugins/admin/static/js/persona-editor.js,sha256=emJSPBXDk54ztWDYOcs-bHAf1qQSIpq11RjV9bt5adA,10382
|
|
56
56
|
mindroot/coreplugins/admin/static/js/plugin-advanced-install.js,sha256=-HDJ3lVuDwj6R-74TfVUo4dUxB8efl13m3R_sUicnJI,8038
|
|
57
57
|
mindroot/coreplugins/admin/static/js/plugin-base.js,sha256=KWp5DqueHtyTxYKbuHMoFpoFXrfMbIjzK4M1ulAR9m8,5095
|
|
58
58
|
mindroot/coreplugins/admin/static/js/plugin-index-browser.js,sha256=P-V4wqlYGxjr7oF2LiD5ti8Is3wtSsKPwpRgRJpT0VI,10028
|
|
@@ -439,7 +439,7 @@ mindroot/coreplugins/admin/static/js/lit-html/node/directives/when.js,sha256=NLe
|
|
|
439
439
|
mindroot/coreplugins/admin/static/js/lit-html/node/directives/when.js.map,sha256=tOonih_-EaqrunhNGshA9xN--WIVdGikjg8MkVp0itQ,1534
|
|
440
440
|
mindroot/coreplugins/admin/templates/admin.jinja2,sha256=H_oDqoWWk0Da0Jre67LIKvB3h30fmjcZz2T5knUyz0k,13272
|
|
441
441
|
mindroot/coreplugins/admin/templates/model-preferences-v2.jinja2,sha256=5J3rXYmtp_yMTFCk85SYN03gc4lbidF0Nip6YcqcIW4,891
|
|
442
|
-
mindroot/coreplugins/agent/agent.py,sha256=
|
|
442
|
+
mindroot/coreplugins/agent/agent.py,sha256=k3G3VaRjGwd1TNYQloytE4NV5cXyVxiuUl1GaMfdTEE,22335
|
|
443
443
|
mindroot/coreplugins/agent/agent.py.bak,sha256=X-EmtrpEpdfo-iUw9gj7mLveRVzAApsDWPTwMAuv7Ww,20715
|
|
444
444
|
mindroot/coreplugins/agent/cmd_start_example.py,sha256=Mdcd9st6viI6-M7a0-zqkw3IxR9FAxIiZ_8G-tLdIJk,1416
|
|
445
445
|
mindroot/coreplugins/agent/command_parser.py,sha256=dgMqtVLPQWE2BU7iyjqwKGy5Gh74jcZkiy1JDs07t4E,13166
|
|
@@ -463,15 +463,16 @@ mindroot/coreplugins/api_keys/static/js/api-key-manager.js,sha256=imqlhd85Z-1e7u
|
|
|
463
463
|
mindroot/coreplugins/chat/__init__.py,sha256=qVdTF1fHZJHwY_ChnPvNFx2Nlg07FHvK0V_JmzfWzdw,230
|
|
464
464
|
mindroot/coreplugins/chat/buwidget_routes.py,sha256=MtwaPX2vEGDylifWOqcx7EAhDw0y1Y3Y91z58EJaLsc,9982
|
|
465
465
|
mindroot/coreplugins/chat/commands.py,sha256=vlgGOvwvjpCbSsW25x4HaeFzeRNWXoEKrdqNpwX_EGg,17077
|
|
466
|
+
mindroot/coreplugins/chat/edit_router.py,sha256=25BStzAIvGhULb_07bZw2aNxnpqabKV6CZHl9xrYdbQ,18629
|
|
466
467
|
mindroot/coreplugins/chat/format_result_msgs.py,sha256=daEdpEyAJIa8b2VkCqSKcw8PaExcB6Qro80XNes_sHA,2
|
|
467
468
|
mindroot/coreplugins/chat/mod.py,sha256=Xydjv3feKJJRbwdiB7raqiQnWtaS_2GcdC9bXYQX3nE,425
|
|
468
469
|
mindroot/coreplugins/chat/models.py,sha256=GRcRuDUAJFpyWERPMxkxUaZ21igNlWeeamriruEKiEQ,692
|
|
469
470
|
mindroot/coreplugins/chat/router.py,sha256=vq9UwYoFoGQMVmDC0TkjH7TWivFwkIe6OU0Scu-dtHg,15998
|
|
470
471
|
mindroot/coreplugins/chat/router_dedup_patch.py,sha256=lDSpVMSd26pXJwrrdirUmR1Jv_N5VHiDzTS8t3XswDU,918
|
|
471
|
-
mindroot/coreplugins/chat/services.py,sha256=
|
|
472
|
+
mindroot/coreplugins/chat/services.py,sha256=cODRoNjE0lrraYBR5fwzMAwB-Y-GBQTr8VCMTzsejlY,22325
|
|
472
473
|
mindroot/coreplugins/chat/utils.py,sha256=BiE14PpsAcQSO5vbU88klHGm8cAXJDXxgVgva-EXybU,155
|
|
473
474
|
mindroot/coreplugins/chat/widget_manager.py,sha256=LrMbZlHqpxbLwdN4XZ4GkLxORwxa1o6IVCrlUDBmGQs,4786
|
|
474
|
-
mindroot/coreplugins/chat/widget_routes.py,sha256=
|
|
475
|
+
mindroot/coreplugins/chat/widget_routes.py,sha256=iV3OwLFnvLDsMHdckJnmVXcUgyyng-zIPNXyK2LAUjc,11802
|
|
475
476
|
mindroot/coreplugins/chat/static/assistant.png,sha256=oAt1ctkFKLSPBoAZGNnSixooW9ANVIk1GwniauVWDXo,215190
|
|
476
477
|
mindroot/coreplugins/chat/static/mindgen.png,sha256=fN3E3oOFvAGYjJq-Pvg2f75jIMv7kg5WRU0EeEbxCWg,235353
|
|
477
478
|
mindroot/coreplugins/chat/static/mindroot_logo.png,sha256=ZfPlCqCjU0_TXte5gvEx81zRKge--l_z2y0AArKl0as,17823
|
|
@@ -887,7 +888,7 @@ mindroot/coreplugins/chat/static/js/lit-html/node/directives/until.js,sha256=j1W
|
|
|
887
888
|
mindroot/coreplugins/chat/static/js/lit-html/node/directives/until.js.map,sha256=7xiwSZ7_fGtr5XwW-10Dzs8n9QE2VUfXaZ0Sd6d82L0,6567
|
|
888
889
|
mindroot/coreplugins/chat/static/js/lit-html/node/directives/when.js,sha256=NLe0NJ-6jqjVDUrT_DzmSpREsRaLo1yarzdYcV_5xHY,181
|
|
889
890
|
mindroot/coreplugins/chat/static/js/lit-html/node/directives/when.js.map,sha256=tOonih_-EaqrunhNGshA9xN--WIVdGikjg8MkVp0itQ,1534
|
|
890
|
-
mindroot/coreplugins/chat/templates/chat.jinja2,sha256=
|
|
891
|
+
mindroot/coreplugins/chat/templates/chat.jinja2,sha256=54yUJ7v-fE2r8iuXYleqznChApXKNQSuEJgIlZgyrCk,3256
|
|
891
892
|
mindroot/coreplugins/chat_avatar/__init__.py,sha256=MsSFjiLMLJZ7QhUPpVBWKiyDnCzryquRyr329NoCACI,2
|
|
892
893
|
mindroot/coreplugins/chat_avatar/inject/chat.jinja2,sha256=TDSSt_SdOOW4EJMQK7fA_L2W5GNbDICRmXyqSsw0wuE,1093
|
|
893
894
|
mindroot/coreplugins/check_list/__init__.py,sha256=SaaGvnpz37xRM7DjGWBz5CD27Jh2UVdPLGoVUAFrUSY,77
|
|
@@ -2196,7 +2197,7 @@ mindroot/lib/buchatlog2.py,sha256=Va9FteBWePEjWD9OZcw-OtQfEb-IoCVGTmJeMRaX9is,13
|
|
|
2196
2197
|
mindroot/lib/buchatlog3.py,sha256=SAvcK2m_CW0Jw8p1pqnbrTexcx24PotrsJTqvQ_D290,24573
|
|
2197
2198
|
mindroot/lib/butemplates.py,sha256=gfHGPTOjvoEenXsR7xokNuqMjOAPuC2DawheH1Ae4bU,12196
|
|
2198
2199
|
mindroot/lib/chatcontext.py,sha256=CXk-pX-7RG3NiRFsAZWERWxnuFJOHH7FHtOLm-kGRXE,12437
|
|
2199
|
-
mindroot/lib/chatlog.py,sha256=
|
|
2200
|
+
mindroot/lib/chatlog.py,sha256=ACdixKTn_GlVVfB00fNlphNPMFnRrum_za_mQfGPQoQ,26372
|
|
2200
2201
|
mindroot/lib/chatlog_optimized.py,sha256=rL7KBP-V4_cGgMLihxPm3HoKcjFEyA1uEtPtqvkOa3A,20011
|
|
2201
2202
|
mindroot/lib/json_escape.py,sha256=5cAmAdNbnYX2uyfQcnse2fFtNI0CdB-AfZ23RwaDm-k,884
|
|
2202
2203
|
mindroot/lib/model_selector.py,sha256=Wz-8NZoiclmnhLeCNnI3WCuKFmjsO5HE4bK5F8GpZzU,1397
|
|
@@ -2250,9 +2251,9 @@ mindroot/protocols/services/stream_chat.py,sha256=fMnPfwaB5fdNMBLTEg8BXKAGvrELKH
|
|
|
2250
2251
|
mindroot/registry/__init__.py,sha256=40Xy9bmPHsgdIrOzbtBGzf4XMqXVi9P8oZTJhn0r654,151
|
|
2251
2252
|
mindroot/registry/component_manager.py,sha256=WZFNPg4SNvpqsM5NFiC2DpgmrJQCyR9cNhrCBpp30Qk,995
|
|
2252
2253
|
mindroot/registry/data_access.py,sha256=81In5TwETpaqnnY1_-tBQM7rfWvUxZUZkG7lEelRUfU,5321
|
|
2253
|
-
mindroot-10.
|
|
2254
|
-
mindroot-10.
|
|
2255
|
-
mindroot-10.
|
|
2256
|
-
mindroot-10.
|
|
2257
|
-
mindroot-10.
|
|
2258
|
-
mindroot-10.
|
|
2254
|
+
mindroot-10.6.0.dist-info/licenses/LICENSE,sha256=8plAmZh8y9ccuuqFFz4kp7G-cO_qsPgAOoHNvabSB4U,1070
|
|
2255
|
+
mindroot-10.6.0.dist-info/METADATA,sha256=B_jgoqAFx9YKO_ddBGR0_rVBewpVyTae8JJhUxQI_LY,1035
|
|
2256
|
+
mindroot-10.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
2257
|
+
mindroot-10.6.0.dist-info/entry_points.txt,sha256=0bpyjMccLttx6VcjDp6zfJPN0Kk0rffor6IdIbP0j4c,50
|
|
2258
|
+
mindroot-10.6.0.dist-info/top_level.txt,sha256=gwKm7DmNjhdrCJTYCrxa9Szne4lLpCtrEBltfsX-Mm8,9
|
|
2259
|
+
mindroot-10.6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|