feat: add guided exploration and share user-started MCP backend
This commit is contained in:
1 parent
f86db1ced6
commit
ee256ad7ce
144 files changed
+8625
-2075
No files matched your search
@@ -1119,6 +1119,34 @@ def test_history_filters_all_persisted_rows_and_escapes_like(client, isolated):
|
||||
)
|
||||
|
||||
|
||||
def test_history_returns_persisted_execution_mode_without_inventing_legacy_mode(client, isolated):
|
||||
with isolated.connection() as connection:
|
||||
for sid, device_info in [
|
||||
("flash", '{"profile":"flash"}'),
|
||||
("pro", '{"profile":"pro"}'),
|
||||
("legacy", "not json"),
|
||||
]:
|
||||
connection.execute(
|
||||
"INSERT INTO sessions(session_id,initial_goal,start_time,status,device_info) VALUES (?,?,?,?,?)",
|
||||
(sid, sid, 100, "completed", device_info),
|
||||
)
|
||||
connection.commit()
|
||||
isolated.save_job(
|
||||
{
|
||||
"session_id": "queued-only",
|
||||
"status": "failed",
|
||||
"case": {"name": "no device"},
|
||||
"task_config": {"profile": "pro"},
|
||||
"created_at": 100,
|
||||
}
|
||||
)
|
||||
rows = {row["session_id"]: row for row in client.get("/api/automation/history").json()["items"]}
|
||||
assert rows["flash"]["profile"] == "flash"
|
||||
assert rows["pro"]["profile"] == "pro"
|
||||
assert rows["legacy"]["profile"] is None
|
||||
assert rows["queued-only"]["profile"] == "pro"
|
||||
|
||||
|
||||
def test_batch_submission_is_atomic_and_idempotent(client, isolated):
|
||||
value = payload(request_id="same-request")
|
||||
first = client.post("/api/automation/batches", json=value)
|
||||
@@ -1145,3 +1173,131 @@ def test_removed_replay_is_inert_but_history_survives(client, isolated):
|
||||
assert client.post(f"/api/automation/batches/{batch['id']}/retry_failed").status_code == 409
|
||||
assert client.get("/api/automation/workflows").status_code == 404
|
||||
assert client.get("/api/automation/history").json()["total"] == 2
|
||||
|
||||
|
||||
def test_exploration_history_groups_before_pagination_and_preserves_validations(client, isolated):
|
||||
with isolated.connection() as conn:
|
||||
for i, kind in enumerate(["exploration", "exploration", "validation", "validation"]):
|
||||
sid = f"run-{i}"
|
||||
conn.execute(
|
||||
"INSERT INTO sessions(session_id,initial_goal,start_time,status) VALUES (?,?,?,?)",
|
||||
(sid, "目标", i + 1, "completed"),
|
||||
)
|
||||
record = {
|
||||
"exploration_id": "conversation",
|
||||
"kind": kind,
|
||||
"created_at": i + 1,
|
||||
"status": "completed",
|
||||
"case_version": 1,
|
||||
}
|
||||
conn.execute(
|
||||
"INSERT INTO automation_records VALUES (?,?,?,?)",
|
||||
("exploration_run", sid, json.dumps(record), i + 1),
|
||||
)
|
||||
conn.commit()
|
||||
all_rows = client.get("/api/automation/history").json()
|
||||
assert all_rows["total"] == 3
|
||||
assert {r["session_id"] for r in all_rows["items"]} == {"run-1", "run-2", "run-3"}
|
||||
page = client.get("/api/automation/history?batch_id=__exploration__&size=1").json()
|
||||
assert page["total"] == 1
|
||||
assert page["items"][0]["session_id"] == "run-1"
|
||||
assert page["items"][0]["exploration_run_count"] == 2
|
||||
ordinary = client.get("/api/automation/history?batch_id=__single__").json()
|
||||
assert ordinary["total"] == 2
|
||||
with isolated.connection() as conn:
|
||||
conn.execute(
|
||||
"INSERT INTO automation_records VALUES (?,?,?,?)",
|
||||
(
|
||||
"exploration",
|
||||
"conversation",
|
||||
json.dumps({"status": "paused", "active_run_id": None}),
|
||||
5,
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
assert client.get("/api/automation/history?batch_id=__exploration__").json()["total"] == 0
|
||||
|
||||
|
||||
def test_validation_history_uses_case_title_after_loading_its_job(client, isolated):
|
||||
from artemis.config.exploration import ExplorationConfig
|
||||
from artemis.exploration.models import CaseDraft
|
||||
from artemis.exploration.store import ExplorationStore
|
||||
|
||||
repository = ExplorationStore(isolated)
|
||||
session = repository.create("探索设置", ExplorationConfig(), {}, "create")
|
||||
repository.save_case(
|
||||
session["id"], CaseDraft(name="深色主题检查", goal="完整执行提示词"), 0, "save"
|
||||
)
|
||||
run = repository.reserve_run(
|
||||
session["id"],
|
||||
"validate",
|
||||
kind="validation",
|
||||
goal="完整执行提示词",
|
||||
task_config={},
|
||||
case_version=1,
|
||||
)
|
||||
isolated.save_job(
|
||||
{
|
||||
"session_id": run["id"],
|
||||
"status": "completed",
|
||||
"name": "完整执行提示词",
|
||||
"goal": "完整执行提示词",
|
||||
}
|
||||
)
|
||||
with isolated.connection() as conn:
|
||||
conn.execute(
|
||||
"INSERT INTO sessions(session_id,initial_goal,start_time,status) VALUES (?,?,?,?)",
|
||||
(run["id"], "完整执行提示词", 1, "completed"),
|
||||
)
|
||||
conn.commit()
|
||||
rows = client.get("/api/automation/history?q=深色主题检查").json()["items"]
|
||||
assert len(rows) == 1
|
||||
assert rows[0]["name"] == "深色主题检查"
|
||||
assert rows[0]["initial_goal"] == "完整执行提示词"
|
||||
assert rows[0]["case_version"] == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_queued_exploration_keeps_identity_without_a_session_row(isolated, monkeypatch):
|
||||
from apps.admin_console.routers import tasks
|
||||
from artemis.exploration.store import ExplorationStore
|
||||
from artemis.exploration.models import CreateExploration
|
||||
from apps.admin_console.services.exploration_service import ExplorationService
|
||||
|
||||
service = ExplorationService(ExplorationStore(isolated))
|
||||
session = service.create(CreateExploration(goal="排队探索", request_id="create"))
|
||||
run = service.repository.reserve_run(
|
||||
session["id"],
|
||||
"start",
|
||||
kind="exploration",
|
||||
goal="排队探索",
|
||||
task_config={"model": "gpt-6.1-sol"},
|
||||
)
|
||||
queue_item = {"session_id": run["id"], "goal": run["goal"], "status": "pending"}
|
||||
from types import SimpleNamespace
|
||||
|
||||
monkeypatch.setattr(
|
||||
tasks,
|
||||
"state",
|
||||
SimpleNamespace(
|
||||
is_running=False,
|
||||
queue_items=[],
|
||||
queue_tasks=[queue_item],
|
||||
active_runs={},
|
||||
active_connections={},
|
||||
active_session_id=None,
|
||||
current_goal=None,
|
||||
current_profile=None,
|
||||
ipc_port=0,
|
||||
),
|
||||
)
|
||||
monkeypatch.setattr(tasks, "store", isolated)
|
||||
monkeypatch.setattr(tasks.task_queue_service, "ensure_worker_running", lambda: None)
|
||||
monkeypatch.setattr(tasks, "session_repo", Mock(get_latest_session=lambda: None))
|
||||
monkeypatch.setattr(tasks.DeviceExecutionLock, "get_active_owner", lambda: None)
|
||||
monkeypatch.setattr(tasks.DeviceExecutionLock, "get_active_owners", lambda: {})
|
||||
monkeypatch.setattr(tasks.DeviceExecutionLock, "get_queued_tasks", lambda: [])
|
||||
result = await tasks.get_status()
|
||||
assert result["queue"][0]["exploration_id"] == session["id"]
|
||||
assert result["queue"][0]["exploration_kind"] == "exploration"
|
||||
assert "exploration_id" not in queue_item # Do not mutate scheduler state.
|
||||
@@ -93,7 +93,14 @@ def test_new_install_uses_playones_endpoint_without_overriding_saved_url(monkeyp
|
||||
|
||||
def test_model_presets_include_flash_pro_and_luna_without_restricting_custom_names():
|
||||
presets = service.get_model_settings()["model_presets"]
|
||||
assert presets == ["gpt-5.6-luna", "gpt-6-luna", "mimo-v2.6-flash", "mimo-v2.6-pro"]
|
||||
assert presets == [
|
||||
"gpt-6.1-sol",
|
||||
"gpt-6-astra",
|
||||
"gpt-5.6-luna",
|
||||
"gpt-6-luna",
|
||||
"mimo-v2.6-flash",
|
||||
"mimo-v2.6-pro",
|
||||
]
|
||||
assert service.ModelSettingsUpdate(model="custom/model-v2").model == "custom/model-v2"
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user