mirror of
https://github.com/khoj-ai/khoj.git
synced 2024-11-30 10:53:02 +01:00
Rename scheduled task to automations across code and UX
- Fix query, subject parameters passed to email template - Show 12 hour scheduled time in automation created chat message
This commit is contained in:
parent
230d160602
commit
2f9241b5a3
9 changed files with 128 additions and 134 deletions
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Khoj AI - Task</title>
|
||||
<title>Khoj AI - Automation</title>
|
||||
</head>
|
||||
<body>
|
||||
<body style="font-family: 'Verdana', sans-serif; font-weight: 400; font-style: normal; padding: 0; text-align: left; width: 600px; margin: 20px auto;">
|
||||
|
@ -13,7 +13,7 @@
|
|||
<div>
|
||||
<h1 style="color: #333; font-size: large; font-weight: bold; margin: 0; line-height: 1.5; background-color: #fee285; padding: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.5);">Your Open, Personal AI</h1>
|
||||
<p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">Hey {{name}}! </p>
|
||||
<p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">I've shared your scheduled task results below:</p>
|
||||
<p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">I've shared your automation results below:</p>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; grid-gap: 12px; margin-top: 20px;">
|
||||
<div style="border: 1px solid black; border-radius: 8px; padding: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0); margin-top: 20px;">
|
||||
|
@ -23,8 +23,8 @@
|
|||
<p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">{{result}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">The scheduled query I ran on your behalf: {query}</p>
|
||||
<p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">You can view, delete and manage your scheduled tasks via <a href="https://app.khoj.dev/configure#tasks">the settings page</a></p>
|
||||
<p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">The automation query I ran on your behalf: {{query}}</p>
|
||||
<p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">You can view, delete your automations via <a href="https://app.khoj.dev/configure#tasks">the settings page</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<p style="color: #333; font-size: large; margin-top: 20px; padding: 0; line-height: 1.5;">- Khoj</p>
|
||||
|
|
|
@ -272,17 +272,17 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="tasks" class="section">
|
||||
<h2 class="section-title">Scheduled Tasks</h2>
|
||||
<div id="scheduled-tasks" class="api-settings">
|
||||
<div id="automations" class="section">
|
||||
<h2 class="section-title">Automations</h2>
|
||||
<div id="automations" class="api-settings">
|
||||
<div class="card-title-row">
|
||||
<img class="card-icon" src="/static/assets/icons/key.svg" alt="Scheduled Tasks">
|
||||
<h3 class="card-title">Tasks</h3>
|
||||
<img class="card-icon" src="/static/assets/icons/key.svg" alt="Automations">
|
||||
<h3 class="card-title">Automations</h3>
|
||||
</div>
|
||||
<div class="card-description-row">
|
||||
<p id="tasks-settings-card-description" class="card-description">Manage your scheduled tasks</p>
|
||||
<p id="tasks-settings-card-description" class="card-description">Manage your automations</p>
|
||||
</div>
|
||||
<table id="scheduled-tasks-table">
|
||||
<table id="automations-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
|
@ -292,10 +292,10 @@
|
|||
<th scope="col">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="scheduled-tasks-list"></tbody>
|
||||
<tbody id="automations-list"></tbody>
|
||||
</table>
|
||||
<div class="card-action-row">
|
||||
<button class="card-button happy" id="create-scheduled-task" onclick="createScheduledTask()">
|
||||
<button class="card-button happy" id="create-automation" onclick="createAutomation()">
|
||||
Create Task
|
||||
</button>
|
||||
</div>
|
||||
|
@ -661,46 +661,42 @@
|
|||
// List user's API keys on page load
|
||||
listApiKeys();
|
||||
|
||||
function deleteTask(taskId) {
|
||||
const scheduledTaskList = document.getElementById("scheduled-tasks-list");
|
||||
fetch(`/api/task?task_id=${taskId}`, {
|
||||
function deleteAutomation(automationId) {
|
||||
const AutomationList = document.getElementById("automations-list");
|
||||
fetch(`/api/automation?automation_id=${automationId}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
.then(response => {
|
||||
if (response.status == 200) {
|
||||
const scheduledTaskItem = document.getElementById(`scheduled-task-item-${taskId}`);
|
||||
scheduledTaskList.removeChild(scheduledTaskItem);
|
||||
const AutomationItem = document.getElementById(`automation-item-${automationId}`);
|
||||
AutomationList.removeChild(AutomationItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function generateTaskRow(taskObj) {
|
||||
let taskId = taskObj.id;
|
||||
let taskSchedulingRequest = taskObj.scheduling_request;
|
||||
let taskQuery = taskObj.query_to_run;
|
||||
let taskSubject = taskObj.subject;
|
||||
let taskNextRun = `Next run at ${taskObj.next}`;
|
||||
let taskSchedule = taskObj.schedule;
|
||||
function generateAutomationRow(automationObj) {
|
||||
let automationId = automationObj.id;
|
||||
let automationNextRun = `Next run at ${automationObj.next}`;
|
||||
return `
|
||||
<tr id="scheduled-task-item-${taskId}">
|
||||
<td><b>${taskSubject}</b></td>
|
||||
<td><b>${taskSchedulingRequest}</b></td>
|
||||
<td><b>${taskQuery}</b></td>
|
||||
<td id="scheduled-task-${taskId}" title="${taskNextRun}">${taskSchedule}</td>
|
||||
<tr id="automation-item-${automationId}">
|
||||
<td><b>${automationObj.subject}</b></td>
|
||||
<td><b>${automationObj.scheduling_request}</b></td>
|
||||
<td><b>${automationObj.query_to_run}</b></td>
|
||||
<td id="automation-${automationId}" title="${automationNextRun}">${automationObj.schedule}</td>
|
||||
<td>
|
||||
<img onclick="deleteTask('${taskId}')" class="configured-icon api-key-action enabled" src="/static/assets/icons/trash-solid.svg" alt="Delete Task" title="Delete Task">
|
||||
<img onclick="deleteAutomation('${automationId}')" class="configured-icon api-key-action enabled" src="/static/assets/icons/trash-solid.svg" alt="Delete Automation" title="Delete Automation">
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
|
||||
function listScheduledTasks() {
|
||||
const scheduledTasksList = document.getElementById("scheduled-tasks-list");
|
||||
fetch('/api/tasks')
|
||||
function listAutomations() {
|
||||
const AutomationsList = document.getElementById("automations-list");
|
||||
fetch('/api/automations')
|
||||
.then(response => response.json())
|
||||
.then(tasks => {
|
||||
if (!tasks?.length > 0) return;
|
||||
scheduledTasksList.innerHTML = tasks.map(generateTaskRow).join("");
|
||||
.then(automations => {
|
||||
if (!automations?.length > 0) return;
|
||||
AutomationsList.innerHTML = automations.map(generateAutomationRow).join("");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -713,8 +709,8 @@
|
|||
});
|
||||
}
|
||||
|
||||
// List user's scheduled tasks on page load
|
||||
listScheduledTasks();
|
||||
// List user's automations on page load
|
||||
listAutomations();
|
||||
|
||||
function removeFile(path) {
|
||||
fetch('/api/config/data/file?filename=' + path, {
|
||||
|
|
|
@ -507,7 +507,7 @@ Khoj:
|
|||
""".strip()
|
||||
)
|
||||
|
||||
# Schedule task
|
||||
# Automations
|
||||
# --
|
||||
crontime_prompt = PromptTemplate.from_template(
|
||||
"""
|
||||
|
@ -525,7 +525,7 @@ AI: Here is one I found: "It's not denial. I'm just selective about the reality
|
|||
User: Hahah, nice! Show a new one every morning.
|
||||
Khoj: {{
|
||||
"crontime": "0 9 * * *",
|
||||
"query": "/task Share a funny Calvin and Hobbes or Bill Watterson quote from my notes",
|
||||
"query": "/automated_task Share a funny Calvin and Hobbes or Bill Watterson quote from my notes",
|
||||
"subject": "Your Calvin and Hobbes Quote for the Day"
|
||||
}}
|
||||
|
||||
|
@ -534,7 +534,7 @@ Khoj: {{
|
|||
User: Every monday evening at 6 share the top posts on hacker news from last week. Format it as a newsletter
|
||||
Khoj: {{
|
||||
"crontime": "0 18 * * 1",
|
||||
"query": "/task Top posts last week on Hacker News",
|
||||
"query": "/automated_task Top posts last week on Hacker News",
|
||||
"subject": "Your Weekly Top Hacker News Posts Newsletter"
|
||||
}}
|
||||
|
||||
|
@ -545,7 +545,7 @@ AI: The latest released Khoj python package version is 1.5.0.
|
|||
User: Notify me when version 2.0.0 is released
|
||||
Khoj: {{
|
||||
"crontime": "0 10 * * *",
|
||||
"query": "/task What is the latest released version of the Khoj python package?",
|
||||
"query": "/automated_task What is the latest released version of the Khoj python package?",
|
||||
"subject": "Khoj Python Package Version 2.0.0 Release"
|
||||
}}
|
||||
|
||||
|
@ -554,7 +554,7 @@ Khoj: {{
|
|||
User: Tell me the latest local tech news on the first sunday of every month
|
||||
Khoj: {{
|
||||
"crontime": "0 8 1-7 * 0",
|
||||
"query": "/task Find the latest local tech, AI and engineering news. Format it as a newsletter.",
|
||||
"query": "/automated_task Find the latest local tech, AI and engineering news. Format it as a newsletter.",
|
||||
"subject": "Your Monthly Dose of Local Tech News"
|
||||
}}
|
||||
|
||||
|
@ -563,7 +563,7 @@ Khoj: {{
|
|||
User: Inform me when the national election results are declared. Run task at 4pm every thursday.
|
||||
Khoj: {{
|
||||
"crontime": "0 16 * * 4",
|
||||
"query": "/task Check if the Indian national election results are officially declared",
|
||||
"query": "/automated_task Check if the Indian national election results are officially declared",
|
||||
"subject": "Indian National Election Results Declared"
|
||||
}}
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ def save_to_conversation_log(
|
|||
intent_type: str = "remember",
|
||||
client_application: ClientApplication = None,
|
||||
conversation_id: int = None,
|
||||
job_id: str = None,
|
||||
automation_id: str = None,
|
||||
):
|
||||
user_message_time = user_message_time or datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
updated_conversation = message_to_log(
|
||||
|
@ -113,7 +113,7 @@ def save_to_conversation_log(
|
|||
"context": compiled_references,
|
||||
"intent": {"inferred-queries": inferred_queries, "type": intent_type},
|
||||
"onlineContext": online_results,
|
||||
"jobId": job_id,
|
||||
"automationId": automation_id,
|
||||
},
|
||||
conversation_log=meta_log.get("chat", []),
|
||||
)
|
||||
|
|
|
@ -391,59 +391,59 @@ def user_info(request: Request) -> Response:
|
|||
return Response(content=json.dumps(user_info), media_type="application/json", status_code=200)
|
||||
|
||||
|
||||
@api.get("/tasks", response_class=Response)
|
||||
@api.get("/automations", response_class=Response)
|
||||
@requires(["authenticated"])
|
||||
def get_jobs(request: Request) -> Response:
|
||||
def get_automations(request: Request) -> Response:
|
||||
user: KhojUser = request.user.object
|
||||
tasks: list[Job] = state.scheduler.get_jobs()
|
||||
automations: list[Job] = state.scheduler.get_jobs()
|
||||
|
||||
# Collate all tasks assigned by user that are still active
|
||||
tasks_info = []
|
||||
for task in tasks:
|
||||
if task.id.startswith(f"job_{user.uuid}_"):
|
||||
task_metadata = json.loads(task.name)
|
||||
schedule = (
|
||||
f'{cron_descriptor.get_description(task_metadata["crontime"])} {task.next_run_time.strftime("%Z")}'
|
||||
)
|
||||
tasks_info.append(
|
||||
# Collate all automations created by user that are still active
|
||||
automations_info = []
|
||||
for automation in automations:
|
||||
if automation.id.startswith(f"automation_{user.uuid}_"):
|
||||
automation_metadata = json.loads(automation.name)
|
||||
crontime = automation_metadata["crontime"]
|
||||
timezone = automation.next_run_time.strftime("%Z")
|
||||
schedule = f"{cron_descriptor.get_description(crontime)} {timezone}"
|
||||
automations_info.append(
|
||||
{
|
||||
"id": task.id,
|
||||
"subject": task_metadata["subject"],
|
||||
"query_to_run": re.sub(r"^/task\s*", "", task_metadata["query_to_run"]),
|
||||
"scheduling_request": task_metadata["scheduling_request"],
|
||||
"id": automation.id,
|
||||
"subject": automation_metadata["subject"],
|
||||
"query_to_run": re.sub(r"^/automated_task\s*", "", automation_metadata["query_to_run"]),
|
||||
"scheduling_request": automation_metadata["scheduling_request"],
|
||||
"schedule": schedule,
|
||||
"next": task.next_run_time.strftime("%Y-%m-%d %I:%M %p %Z"),
|
||||
"next": automation.next_run_time.strftime("%Y-%m-%d %I:%M %p %Z"),
|
||||
}
|
||||
)
|
||||
|
||||
# Return tasks information as a JSON response
|
||||
return Response(content=json.dumps(tasks_info), media_type="application/json", status_code=200)
|
||||
return Response(content=json.dumps(automations_info), media_type="application/json", status_code=200)
|
||||
|
||||
|
||||
@api.delete("/task", response_class=Response)
|
||||
@api.delete("/automation", response_class=Response)
|
||||
@requires(["authenticated"])
|
||||
def delete_job(request: Request, task_id: str) -> Response:
|
||||
def delete_automation(request: Request, automation_id: str) -> Response:
|
||||
user: KhojUser = request.user.object
|
||||
|
||||
# Perform validation checks
|
||||
# Check if user is allowed to delete this task id
|
||||
if not task_id.startswith(f"job_{user.uuid}_"):
|
||||
# Check if user is allowed to delete this automation id
|
||||
if not automation_id.startswith(f"automation_{user.uuid}_"):
|
||||
return Response(content="Unauthorized job deletion request", status_code=403)
|
||||
# Check if task with this task id exist
|
||||
task: Job = state.scheduler.get_job(job_id=task_id)
|
||||
if not task:
|
||||
# Check if automation with this id exist
|
||||
automation: Job = state.scheduler.get_job(job_id=automation_id)
|
||||
if not automation:
|
||||
return Response(content="Invalid job", status_code=403)
|
||||
|
||||
# Collate info about user task to be deleted
|
||||
task_metadata = json.loads(task.name)
|
||||
task_info = {
|
||||
"id": task.id,
|
||||
"name": task_metadata["inferred_query"],
|
||||
"next": task.next_run_time.strftime("%Y-%m-%d %H:%MS"),
|
||||
automation_metadata = json.loads(automation.name)
|
||||
automation_info = {
|
||||
"id": automation.id,
|
||||
"name": automation_metadata["query_to_run"],
|
||||
"next": automation.next_run_time.strftime("%Y-%m-%d %I:%M %p %Z"),
|
||||
}
|
||||
|
||||
# Delete job
|
||||
task.remove()
|
||||
automation.remove()
|
||||
|
||||
# Return delete task information as a JSON response
|
||||
return Response(content=json.dumps(task_info), media_type="application/json", status_code=200)
|
||||
# Return deleted automation information as a JSON response
|
||||
return Response(content=json.dumps(automation_info), media_type="application/json", status_code=200)
|
||||
|
|
|
@ -37,7 +37,7 @@ from khoj.routers.helpers import (
|
|||
agenerate_chat_response,
|
||||
aget_relevant_information_sources,
|
||||
aget_relevant_output_modes,
|
||||
create_scheduled_task,
|
||||
create_automation,
|
||||
get_conversation_command,
|
||||
is_ready_to_chat,
|
||||
text_to_image,
|
||||
|
@ -217,6 +217,7 @@ async def chat_options(
|
|||
) -> Response:
|
||||
cmd_options = {}
|
||||
for cmd in ConversationCommand:
|
||||
if cmd in command_descriptions:
|
||||
cmd_options[cmd.value] = command_descriptions[cmd]
|
||||
|
||||
update_telemetry_state(
|
||||
|
@ -373,14 +374,14 @@ async def websocket_endpoint(
|
|||
continue
|
||||
|
||||
meta_log = conversation.conversation_log
|
||||
is_task = conversation_commands == [ConversationCommand.Task]
|
||||
is_automated_task = conversation_commands == [ConversationCommand.AutomatedTask]
|
||||
|
||||
if conversation_commands == [ConversationCommand.Default] or is_task:
|
||||
if conversation_commands == [ConversationCommand.Default] or is_automated_task:
|
||||
conversation_commands = await aget_relevant_information_sources(q, meta_log)
|
||||
conversation_commands_str = ", ".join([cmd.value for cmd in conversation_commands])
|
||||
await send_status_update(f"**🗃️ Chose Data Sources to Search:** {conversation_commands_str}")
|
||||
|
||||
mode = await aget_relevant_output_modes(q, meta_log, is_task)
|
||||
mode = await aget_relevant_output_modes(q, meta_log, is_automated_task)
|
||||
await send_status_update(f"**🧑🏾💻 Decided Response Mode:** {mode.value}")
|
||||
if mode not in conversation_commands:
|
||||
conversation_commands.append(mode)
|
||||
|
@ -389,29 +390,31 @@ async def websocket_endpoint(
|
|||
await conversation_command_rate_limiter.update_and_check_if_valid(websocket, cmd)
|
||||
q = q.replace(f"/{cmd.value}", "").strip()
|
||||
|
||||
if ConversationCommand.Reminder in conversation_commands:
|
||||
if ConversationCommand.Automation in conversation_commands:
|
||||
try:
|
||||
job, crontime, inferred_query, subject = await create_scheduled_task(
|
||||
automation, crontime, query_to_run, subject = await create_automation(
|
||||
q, location, timezone, user, websocket.url, meta_log
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error scheduling task {q} for {user.email}: {e}")
|
||||
await send_complete_llm_response(f"Unable to schedule task. Ensure the task doesn't already exist.")
|
||||
await send_complete_llm_response(
|
||||
f"Unable to create automation. Ensure the automation doesn't already exist."
|
||||
)
|
||||
continue
|
||||
# Display next run time in user timezone instead of UTC
|
||||
schedule = f'{cron_descriptor.get_description(crontime)} {job.next_run_time.strftime("%Z")}'
|
||||
next_run_time = job.next_run_time.strftime("%Y-%m-%d %H:%M %Z")
|
||||
# Remove /task prefix from inferred_query
|
||||
unprefixed_inferred_query = re.sub(r"^\/task\s*", "", inferred_query)
|
||||
# Create the scheduled task response
|
||||
schedule = f'{cron_descriptor.get_description(crontime)} {automation.next_run_time.strftime("%Z")}'
|
||||
next_run_time = automation.next_run_time.strftime("%Y-%m-%d %I:%M %p %Z")
|
||||
# Remove /automated_task prefix from inferred_query
|
||||
unprefixed_query_to_run = re.sub(r"^\/automated_task\s*", "", query_to_run)
|
||||
# Create the automation response
|
||||
llm_response = f"""
|
||||
### 🕒 Scheduled Task
|
||||
### 🕒 Automation
|
||||
- Subject: **{subject}**
|
||||
- Query: "{unprefixed_inferred_query}"
|
||||
- Query to Run: "{unprefixed_query_to_run}"
|
||||
- Schedule: `{schedule}`
|
||||
- Next Run At: {next_run_time}
|
||||
|
||||
Manage your tasks [here](/config#tasks).
|
||||
Manage your tasks [here](/config#automations).
|
||||
""".strip()
|
||||
|
||||
await sync_to_async(save_to_conversation_log)(
|
||||
|
@ -420,11 +423,11 @@ Manage your tasks [here](/config#tasks).
|
|||
user,
|
||||
meta_log,
|
||||
user_message_time,
|
||||
intent_type="reminder",
|
||||
intent_type="automation",
|
||||
client_application=websocket.user.client_app,
|
||||
conversation_id=conversation_id,
|
||||
inferred_queries=[inferred_query],
|
||||
job_id=job.id,
|
||||
inferred_queries=[query_to_run],
|
||||
automation_id=automation.id,
|
||||
)
|
||||
common = CommonQueryParamsClass(
|
||||
client=websocket.user.client_app,
|
||||
|
@ -621,7 +624,7 @@ async def chat(
|
|||
else:
|
||||
meta_log = conversation.conversation_log
|
||||
|
||||
is_task = conversation_commands == [ConversationCommand.Task]
|
||||
is_task = conversation_commands == [ConversationCommand.AutomatedTask]
|
||||
|
||||
if conversation_commands == [ConversationCommand.Default] or is_task:
|
||||
conversation_commands = await aget_relevant_information_sources(q, meta_log)
|
||||
|
@ -640,32 +643,32 @@ async def chat(
|
|||
|
||||
user_name = await aget_user_name(user)
|
||||
|
||||
if ConversationCommand.Reminder in conversation_commands:
|
||||
if ConversationCommand.Automation in conversation_commands:
|
||||
try:
|
||||
job, crontime, inferred_query, subject = await create_scheduled_task(
|
||||
automation, crontime, query_to_run, subject = await create_automation(
|
||||
q, location, timezone, user, request.url, meta_log
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error scheduling task {q} for {user.email}: {e}")
|
||||
logger.error(f"Error creating automation {q} for {user.email}: {e}")
|
||||
return Response(
|
||||
content=f"Unable to schedule task. Ensure the task doesn't already exist.",
|
||||
content=f"Unable to create automation. Ensure the automation doesn't already exist.",
|
||||
media_type="text/plain",
|
||||
status_code=500,
|
||||
)
|
||||
# Display next run time in user timezone instead of UTC
|
||||
schedule = f'{cron_descriptor.get_description(crontime)} {job.next_run_time.strftime("%Z")}'
|
||||
next_run_time = job.next_run_time.strftime("%Y-%m-%d %H:%M %Z")
|
||||
# Remove /task prefix from inferred_query
|
||||
unprefixed_inferred_query = re.sub(r"^\/task\s*", "", inferred_query)
|
||||
# Create the scheduled task response
|
||||
schedule = f'{cron_descriptor.get_description(crontime)} {automation.next_run_time.strftime("%Z")}'
|
||||
next_run_time = automation.next_run_time.strftime("%Y-%m-%d %I:%M %p %Z")
|
||||
# Remove /automated_task prefix from inferred_query
|
||||
unprefixed_query_to_run = re.sub(r"^\/automated_task\s*", "", query_to_run)
|
||||
# Create the Automation response
|
||||
llm_response = f"""
|
||||
### 🕒 Scheduled Task
|
||||
### 🕒 Automation
|
||||
- Subject: **{subject}**
|
||||
- Query: "{unprefixed_inferred_query}"
|
||||
- Query to Run: "{unprefixed_query_to_run}"
|
||||
- Schedule: `{schedule}`
|
||||
- Next Run At: {next_run_time}
|
||||
|
||||
Manage your tasks [here](/config#tasks).
|
||||
Manage your automations [here](/config#automations).
|
||||
""".strip()
|
||||
|
||||
await sync_to_async(save_to_conversation_log)(
|
||||
|
@ -674,11 +677,11 @@ Manage your tasks [here](/config#tasks).
|
|||
user,
|
||||
meta_log,
|
||||
user_message_time,
|
||||
intent_type="reminder",
|
||||
intent_type="automation",
|
||||
client_application=request.user.client_app,
|
||||
conversation_id=conversation_id,
|
||||
inferred_queries=[inferred_query],
|
||||
job_id=job.id,
|
||||
inferred_queries=[query_to_run],
|
||||
automation_id=automation.id,
|
||||
)
|
||||
|
||||
if stream:
|
||||
|
|
|
@ -58,7 +58,7 @@ def send_task_email(name, email, query, result, subject):
|
|||
template = env.get_template("task.html")
|
||||
|
||||
html_result = markdown_it.MarkdownIt().render(result)
|
||||
html_content = template.render(name=name, query=query, result=html_result)
|
||||
html_content = template.render(name=name, subject=subject, query=query, result=html_result)
|
||||
|
||||
r = resend.Emails.send(
|
||||
{
|
||||
|
|
|
@ -170,8 +170,8 @@ def get_conversation_command(query: str, any_references: bool = False) -> Conver
|
|||
return ConversationCommand.Online
|
||||
elif query.startswith("/image"):
|
||||
return ConversationCommand.Image
|
||||
elif query.startswith("/task"):
|
||||
return ConversationCommand.Task
|
||||
elif query.startswith("/automated_task"):
|
||||
return ConversationCommand.AutomatedTask
|
||||
# If no relevant notes found for the given query
|
||||
elif not any_references:
|
||||
return ConversationCommand.General
|
||||
|
@ -239,7 +239,7 @@ async def aget_relevant_output_modes(query: str, conversation_history: dict, is_
|
|||
|
||||
for mode, description in mode_descriptions_for_llm.items():
|
||||
# Do not allow tasks to schedule another task
|
||||
if is_task and mode == ConversationCommand.Reminder:
|
||||
if is_task and mode == ConversationCommand.Automation:
|
||||
continue
|
||||
mode_options[mode.value] = description
|
||||
mode_options_str += f'- "{mode.value}": "{description}"\n'
|
||||
|
@ -857,18 +857,14 @@ def should_notify(original_query: str, executed_query: str, ai_response: str) ->
|
|||
response=ai_response,
|
||||
)
|
||||
|
||||
with timer("Chat actor: Decide to notify user of AI response", logger):
|
||||
with timer("Chat actor: Decide to notify user of automation response", logger):
|
||||
try:
|
||||
response = send_message_to_model_wrapper_sync(to_notify_or_not)
|
||||
should_notify_result = "no" not in response.lower()
|
||||
logger.info(
|
||||
f'Decided to {"not " if not should_notify_result else ""}notify user of scheduled task response.'
|
||||
)
|
||||
logger.info(f'Decided to {"not " if not should_notify_result else ""}notify user of automation response.')
|
||||
return should_notify_result
|
||||
except:
|
||||
logger.warning(
|
||||
f"Fallback to notify user of scheduled task response as failed to infer should notify or not."
|
||||
)
|
||||
logger.warning(f"Fallback to notify user of automation response as failed to infer should notify or not.")
|
||||
return True
|
||||
|
||||
|
||||
|
@ -904,7 +900,7 @@ def scheduled_chat(query_to_run: str, scheduling_request: str, subject: str, use
|
|||
return None
|
||||
|
||||
# Extract the AI response from the chat API response
|
||||
cleaned_query = re.sub(r"^/task\s*", "", query_to_run).strip()
|
||||
cleaned_query = re.sub(r"^/automated_task\s*", "", query_to_run).strip()
|
||||
if raw_response.headers.get("Content-Type") == "application/json":
|
||||
response_map = raw_response.json()
|
||||
ai_response = response_map.get("response") or response_map.get("image")
|
||||
|
@ -919,7 +915,7 @@ def scheduled_chat(query_to_run: str, scheduling_request: str, subject: str, use
|
|||
return raw_response
|
||||
|
||||
|
||||
async def create_scheduled_task(
|
||||
async def create_automation(
|
||||
q: str, location: LocationData, timezone: str, user: KhojUser, calling_url: URL, meta_log: dict = {}
|
||||
):
|
||||
user_timezone = pytz.timezone(timezone)
|
||||
|
@ -930,7 +926,7 @@ async def create_scheduled_task(
|
|||
{"query_to_run": query_to_run, "scheduling_request": q, "subject": subject, "crontime": crontime_string}
|
||||
)
|
||||
query_id = hashlib.md5(f"{query_to_run}".encode("utf-8")).hexdigest()
|
||||
job_id = f"job_{user.uuid}_{crontime_string}_{query_id}"
|
||||
job_id = f"automation_{user.uuid}_{crontime_string}_{query_id}"
|
||||
job = state.scheduler.add_job(
|
||||
run_with_process_lock,
|
||||
trigger=trigger,
|
||||
|
|
|
@ -304,8 +304,8 @@ class ConversationCommand(str, Enum):
|
|||
Online = "online"
|
||||
Webpage = "webpage"
|
||||
Image = "image"
|
||||
Reminder = "reminder"
|
||||
Task = "task"
|
||||
Automation = "automation"
|
||||
AutomatedTask = "automated_task"
|
||||
|
||||
|
||||
command_descriptions = {
|
||||
|
@ -315,8 +315,7 @@ command_descriptions = {
|
|||
ConversationCommand.Online: "Search for information on the internet.",
|
||||
ConversationCommand.Webpage: "Get information from webpage links provided by you.",
|
||||
ConversationCommand.Image: "Generate images by describing your imagination in words.",
|
||||
ConversationCommand.Reminder: "Schedule your query to run at a specified time or interval.",
|
||||
ConversationCommand.Task: "Scheduled task running at previously specified schedule.",
|
||||
ConversationCommand.Automation: "Automatically run your query at a specified time or interval.",
|
||||
ConversationCommand.Help: "Display a help message with all available commands and other metadata.",
|
||||
}
|
||||
|
||||
|
@ -330,7 +329,7 @@ tool_descriptions_for_llm = {
|
|||
|
||||
mode_descriptions_for_llm = {
|
||||
ConversationCommand.Image: "Use this if the user is requesting an image or visual response to their query.",
|
||||
ConversationCommand.Reminder: "Use this if the user is requesting a response at a scheduled date or time.",
|
||||
ConversationCommand.Automation: "Use this if the user is requesting a response at a scheduled date or time.",
|
||||
ConversationCommand.Default: "Use this if the other response modes don't seem to fit the query.",
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue