mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-08-05 02:45:28 +00:00
Models that are not shown the full schema sometimes emit action="add" (instead of "create") or wrap task fields in a nested "task" object rather than providing them at the top level. Both patterns fell through to the "Unknown action" error branch, so no task was created even though the model believed it had succeeded. This is the same failure class as the manage_skills schema gap fixed in #4013: the model sees prose describing the tool but not the schema, so it guesses at the action verb and arg shape. Changes: - do_manage_tasks: normalise action="add" to "create" before dispatch - do_manage_tasks: flatten a nested "task" dict into top-level fields, mirroring the existing string-valued "task" alias - do_manage_tasks: use "name" as the "prompt" fallback when prompt is absent, so {"action":"create","name":"foo"} does not return an error - agent_loop.py: expand the local-model tool snippet for manage_tasks with a concrete JSON example and explicit arg descriptions so models without schema access emit the right shape on the first attempt Fixes #5757 Signed-off-by: Christian Sidak <christian@sentineltech.eu> Signed-off-by: Christian-Sidak <61099993+Christian-Sidak@users.noreply.github.com>
120 lines
3.7 KiB
Python
120 lines
3.7 KiB
Python
"""Regression tests for manage_tasks model-write normalisation (#5757).
|
|
|
|
Models that were not shown the full tool schema (e.g. when RAG selects a
|
|
different tool set or a local model is used without function-call schemas)
|
|
sometimes emit action="add" instead of action="create", or wrap task fields in
|
|
a nested "task" object rather than providing them at the top level. Before the
|
|
fix, both patterns fell through to the "unknown action" error branch and the
|
|
task was silently not created.
|
|
|
|
This test module verifies that both calling patterns produce a successfully
|
|
created task, matching the same-class fix applied to manage_skills (#4013).
|
|
"""
|
|
|
|
import json
|
|
import tempfile
|
|
|
|
import pytest
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import sessionmaker
|
|
from sqlalchemy.pool import NullPool
|
|
|
|
from tests.helpers.import_state import clear_fake_database_modules
|
|
|
|
clear_fake_database_modules()
|
|
|
|
import core.database as cdb
|
|
from core.database import ScheduledTask
|
|
from src.tools.system import do_manage_tasks
|
|
|
|
_TMPDB = tempfile.NamedTemporaryFile(suffix=".db", delete=False)
|
|
_ENGINE = create_engine(
|
|
f"sqlite:///{_TMPDB.name}",
|
|
connect_args={"check_same_thread": False},
|
|
poolclass=NullPool,
|
|
)
|
|
cdb.Base.metadata.create_all(_ENGINE)
|
|
_TS = sessionmaker(bind=_ENGINE, autoflush=False, autocommit=False)
|
|
cdb.SessionLocal = _TS
|
|
|
|
|
|
def _count_tasks_named(name):
|
|
db = _TS()
|
|
try:
|
|
return db.query(ScheduledTask).filter(ScheduledTask.name == name).count()
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_action_add_is_treated_as_create():
|
|
"""action='add' must be normalised to 'create' so the task is persisted."""
|
|
out = await do_manage_tasks(
|
|
json.dumps({
|
|
"action": "add",
|
|
"name": "test task add",
|
|
"prompt": "summarise the news",
|
|
"task_type": "llm",
|
|
"trigger_type": "schedule",
|
|
"schedule": "daily",
|
|
}),
|
|
owner="alice",
|
|
)
|
|
assert out["exit_code"] == 0, out
|
|
assert "Created task" in out["response"]
|
|
assert _count_tasks_named("test task add") == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_nested_task_object_is_flattened():
|
|
"""{'action':'add','task':{'name':'foo'}} must be treated as create with name=foo."""
|
|
out = await do_manage_tasks(
|
|
json.dumps({
|
|
"action": "add",
|
|
"task": {
|
|
"name": "test task 2",
|
|
},
|
|
}),
|
|
owner="alice",
|
|
)
|
|
assert out["exit_code"] == 0, out
|
|
assert "Created task" in out["response"]
|
|
assert _count_tasks_named("test task 2") == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_nested_task_object_with_prompt():
|
|
"""Nested task object with explicit prompt field is handled correctly."""
|
|
out = await do_manage_tasks(
|
|
json.dumps({
|
|
"action": "create",
|
|
"task": {
|
|
"name": "daily summary",
|
|
"prompt": "summarise my emails",
|
|
},
|
|
"trigger_type": "schedule",
|
|
"schedule": "daily",
|
|
}),
|
|
owner="alice",
|
|
)
|
|
assert out["exit_code"] == 0, out
|
|
assert "Created task" in out["response"]
|
|
assert _count_tasks_named("daily summary") == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_name_used_as_prompt_fallback_when_prompt_absent():
|
|
"""When prompt is absent but name is present, name is used as the prompt."""
|
|
out = await do_manage_tasks(
|
|
json.dumps({
|
|
"action": "create",
|
|
"name": "my simple task",
|
|
"task_type": "llm",
|
|
"trigger_type": "schedule",
|
|
"schedule": "daily",
|
|
}),
|
|
owner="alice",
|
|
)
|
|
assert out["exit_code"] == 0, out
|
|
assert "Created task" in out["response"]
|
|
assert _count_tasks_named("my simple task") == 1
|