from __future__ import annotations from pathlib import Path from dovla_cil.tasks.library import built_in_toy_tasks from dovla_cil.tasks.validators import validate_task from dovla_cil.utils.io import iter_jsonl from dovla_cil.vlm.client import VLMClient from dovla_cil.vlm.task_generator import ( TaskGenerator, default_task_generation_request, ) class FakeTaskClient(VLMClient): def __init__(self, responses): super().__init__(api_key="fake", model="fake") self.responses = list(responses) self.calls = 0 def chat_json(self, system, user, schema_hint=None): del system, user, schema_hint self.calls += 1 if not self.responses: raise AssertionError("Unexpected VLM call") return self.responses.pop(0) def test_mock_vlm_returns_valid_tasks(monkeypatch) -> None: monkeypatch.setenv("OPENCLAUDE_MOCK", "1") request = default_task_generation_request(num_tasks=3) tasks = TaskGenerator(VLMClient(api_key="fake", model="mock")).generate_tasks(request) assert len(tasks) == 3 for task in tasks: validate_task(task) def test_invalid_generated_task_is_repaired() -> None: valid = built_in_toy_tasks()[0].to_dict() invalid = dict(valid) invalid["success_predicates"] = [{"name": "grasped", "args": ["missing"]}] client = FakeTaskClient([{"tasks": [invalid]}, valid]) request = default_task_generation_request(num_tasks=1) tasks = TaskGenerator(client).generate_tasks(request) assert len(tasks) == 1 assert tasks[0].task_id == valid["task_id"] assert client.calls == 2 def test_invalid_generated_task_is_skipped_when_repair_fails() -> None: valid = built_in_toy_tasks()[0].to_dict() invalid = dict(valid) invalid["success_predicates"] = [{"name": "grasped", "args": ["missing"]}] still_invalid = dict(invalid) client = FakeTaskClient([{"tasks": [invalid]}, still_invalid]) tasks = TaskGenerator(client).generate_tasks(default_task_generation_request(num_tasks=1)) assert tasks == [] assert client.calls == 2 def test_script_runs_in_mock_mode_without_network(tmp_path: Path, monkeypatch) -> None: from scripts.generate_tasks import main out = tmp_path / "tasks.jsonl" monkeypatch.delenv("OPENCLAUDE_API_KEY", raising=False) monkeypatch.delenv("OPENCLAUDE_MODEL", raising=False) result = main(["--num-tasks", "2", "--out", str(out), "--mock", "--seed", "5"]) assert result == 0 rows = list(iter_jsonl(out)) assert len(rows) == 2 assert rows[0]["task_id"]