agentseek-api 0.2.2__tar.gz → 0.2.3__tar.gz
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.
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/.github/workflows/ci.yml +2 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/CHANGELOG.md +15 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/PKG-INFO +8 -3
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/README.md +7 -2
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/README.zh-CN.md +7 -2
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/pyproject.toml +1 -1
- agentseek_api-0.2.3/src/agentseek_api/__init__.py +1 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/database.py +27 -8
- agentseek_api-0.2.3/src/agentseek_api/core/sqlite_checkpointer.py +82 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_metadata_db_config.py +53 -0
- agentseek_api-0.2.3/tests/unit/test_ci_workflow.py +25 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_database_manager.py +27 -13
- agentseek_api-0.2.3/tests/unit/test_sqlite_checkpointer.py +51 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/uv.lock +1 -1
- agentseek_api-0.2.2/src/agentseek_api/__init__.py +0 -1
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/.dockerignore +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/.env.example +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/.github/workflows/live-provider-streaming.yml +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/.github/workflows/release.yml +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/.gitignore +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/AGENTS.md +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/CONTRIBUTING.md +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/LICENSE +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/Makefile +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/README.md +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/assistant_config/agentseek.json +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/assistant_config/graph.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/auth/api_key_auth.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/auth/bearer_token_auth.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/auth/custom_backend.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/auth/jwt.md +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/auth/jwt_auth.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/custom_routes/app.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/docker_ci_auth/auth_backend.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/docker_ci_auth/manifest.json +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/external_graph/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/external_graph/graph.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/external_graph/manifest.json +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/external_graph/run.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/graphs/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/graphs/react_agent/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/graphs/react_agent/graph.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/graphs/store_memory/graph.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/graphs/stress_test/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/graphs/stress_test/graph.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/graphs/stress_tool_agent/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/graphs/stress_tool_agent/graph.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/graphs/subgraph_agent/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/graphs/subgraph_agent/graph.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/graphs/subgraph_hitl_agent/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/graphs/subgraph_hitl_agent/graph.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/live_provider_graphs/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/live_provider_graphs/graph.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/live_provider_graphs/manifest.json +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/minimal_agentseek/agentseek.json +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/minimal_agentseek/graph.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/run_sample_graphs.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/examples/sample_graphs_manifest.json +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/build_live_provider_matrix.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/seekdb_checkpoint_smoke.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/seekdb_embed_launcher.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/test-checkpoints.sh +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/test-cli-dev-samples.sh +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/test-cli-docker.sh +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/test-live-provider.sh +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/test-redis-runtime.sh +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/test-seekdb.sh +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/test_cli_config_autodiscovery.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/test_cli_dev_studio_smoke.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/test_cli_embed_serve_smoke.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/test_cli_serve_smoke.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/test_minimum_cli_dependencies.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/verify_docker_api.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/scripts/verify_worker_concurrency.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/a2a_server.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/api/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/api/assistants.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/api/crons.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/api/runs.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/api/stateless_runs.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/api/store.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/api/streaming.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/api/threads.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/cli.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/constants.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/a2a_config.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/app_loader.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/auth_deps.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/auth_middleware.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/config_file.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/content_type_fix.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/cors_config.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/http_config.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/mcp_config.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/oceanbase_checkpointer.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/orm.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/runtime_store.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/core/store_config.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/dotenv_adapter.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/main.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/mcp_server.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/models/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/models/api.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/models/auth.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/models/protocol.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/process_supervisor.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/runtime_entrypoint.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/scheduler.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/cron_models.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/cron_rrule.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/cron_scheduler.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/cron_service.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/cron_webhooks.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/default_assistants.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/executor.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/langgraph_service.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/redis_queue.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/run_executor.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/run_jobs.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/run_preparation.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/run_state.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/sample_graphs.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/sse.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/stream_modes.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/stream_persistence.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/thread_checkpoint_store.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/thread_protocol.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/services/thread_service.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/settings.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/src/agentseek_api/worker.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/conftest.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/__init__.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/conftest.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/e2e_inprocess_flow.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/e2e_live_http_flow.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/e2e_live_http_multi_graph.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/e2e_live_http_resume_flow.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/fixtures/langgraph.store-e2e.json +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/live_provider_helpers.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/test_a2a_live.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/test_cron_api_live.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/test_cron_scheduler_live.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/test_langsmith_compat_live.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/test_live_provider_api.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/test_live_provider_mcp.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/test_mcp_live.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/e2e/test_real_api_flow.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/fixtures/runtime_settings_probe/sitecustomize.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/fixtures/termination_tree.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_a2a_endpoint.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_agent_card.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_agents_aliases.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_assistants_compat.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_assistants_config_context_sync.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_assistants_crud.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_assistants_ordering.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_auth_coverage_gaps.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_auth_handlers.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_auth_route_enforcement.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_auth_scope_matrix.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_cli_runtime_processes.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_core_api_flow.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_cron_api.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_langsmith_compat_coverage.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_langsmith_compat_extra.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_live_provider_streaming.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_live_redis_queue.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_live_redis_stream_persistence.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_mcp_endpoint.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_protocol_v2_streaming.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_run_cancellation_async.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_runs_compat.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_runs_crud.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_runs_ordering.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_runs_resume.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_runs_streaming.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_runs_streaming_errors.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_runs_validation.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_runs_wait_and_get.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_runs_wait_timeout.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_scheduler_runtime.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_stateless_runs_crud.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_store_api.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_stream_persistence.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_system_endpoints.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_threads_compat.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_threads_crud.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/integration/test_threads_ordering.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_a2a_config.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_a2a_server.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_app_loader.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_assistant_routes_unit.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_auth.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_auth_deps.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_auth_internals.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_checkpoint_guards.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_cli.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_content_type_fix.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_cron_rrule.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_cron_service.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_cron_webhooks.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_custom_routers.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_default_assistants.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_dotenv_adapter.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_examples.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_executor.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_graph_manifest.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_http_config.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_langgraph_service.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_live_provider_e2e_config.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_live_provider_graphs.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_live_provider_matrix.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_mcp_config.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_mcp_server.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_oceanbase_checkpointer.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_openapi_auth_config.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_process_supervisor.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_redis_queue.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_redis_runtime_script.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_run_controls_validation.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_run_executor.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_run_jobs.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_run_preparation.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_run_state.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_runtime_entrypoint.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_runtime_environment.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_runtime_store.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_sample_graphs.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_scheduler.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_scheduler_process.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_seekdb_checkpoint_smoke.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_seekdb_embed_launcher.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_sse_keepalive.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_store_config.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_stream_modes.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_stream_persistence_helpers.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_thread_checkpoint_store.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_thread_protocol.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_thread_routes_unit.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_thread_service.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_worker.py +0 -0
- {agentseek_api-0.2.2 → agentseek_api-0.2.3}/tests/unit/test_worker_concurrency_probe.py +0 -0
|
@@ -91,7 +91,9 @@ jobs:
|
|
|
91
91
|
tests/unit/test_runtime_environment.py
|
|
92
92
|
tests/unit/test_runtime_entrypoint.py
|
|
93
93
|
tests/unit/test_process_supervisor.py
|
|
94
|
+
tests/unit/test_sqlite_checkpointer.py
|
|
94
95
|
tests/integration/test_cli_runtime_processes.py
|
|
96
|
+
tests/integration/test_metadata_db_config.py
|
|
95
97
|
-q
|
|
96
98
|
|
|
97
99
|
- name: Sync embedded SeekDB extra for serve smoke
|
|
@@ -4,6 +4,21 @@ Notable changes to AgentSeek API are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.2.3 - 2026-08-17
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- When `METADATA_DB_BACKEND=sqlite`, store completed-run snapshots in the
|
|
12
|
+
configured SQLite metadata database instead of constructing an OceanBase
|
|
13
|
+
saver and attempting a MySQL-family connection.
|
|
14
|
+
|
|
15
|
+
### Upgrade notes
|
|
16
|
+
|
|
17
|
+
- No manual schema or configuration migration is required. The
|
|
18
|
+
`agentseek_checkpoints` table is created idempotently at startup.
|
|
19
|
+
- This fix covers completed-run snapshots only. LangGraph checkpoint state
|
|
20
|
+
remains in memory when the metadata backend is SQLite.
|
|
21
|
+
|
|
7
22
|
## 0.2.2 - 2026-08-16
|
|
8
23
|
|
|
9
24
|
### Highlights
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agentseek-api
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: AgentSeek API core runtime with OceanBase checkpoints.
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -217,7 +217,7 @@ When the server starts it prints the banner and local URLs:
|
|
|
217
217
|
╠═╣│ ┬├┤ │││ │ ╚═╗├┤ ├┤ ├┴┐
|
|
218
218
|
╩ ╩└─┘└─┘┘└┘ ┴ ╚═╝└─┘└─┘┴ ┴
|
|
219
219
|
|
|
220
|
-
AgentSeek v0.2.
|
|
220
|
+
AgentSeek v0.2.3
|
|
221
221
|
|
|
222
222
|
- 🚀 API: http://localhost:2024
|
|
223
223
|
- 📚 Docs: http://localhost:2024/docs
|
|
@@ -691,7 +691,12 @@ parent api build --config ./langgraph.json -t my-api:dev
|
|
|
691
691
|
- `METADATA_DB_BACKEND=auto` normalizes drivers:
|
|
692
692
|
- PostgreSQL: `postgresql+asyncpg://...`
|
|
693
693
|
- OceanBase / MySQL: `mysql+aiomysql://...`
|
|
694
|
-
-
|
|
694
|
+
- SQLite: `sqlite+aiosqlite://...`
|
|
695
|
+
- Completed-run snapshots default to OceanBase / seekdb settings. With
|
|
696
|
+
`METADATA_DB_BACKEND=sqlite`, they are stored in the configured SQLite
|
|
697
|
+
metadata database without constructing an OceanBase saver.
|
|
698
|
+
- The SQLite metadata path still uses an in-memory LangGraph checkpointer; it
|
|
699
|
+
does not make LangGraph graph checkpoints durable.
|
|
695
700
|
- Auth: configure via `agentseek.json` `"auth.path"` or `AUTH_MODULE_PATH` env var.
|
|
696
701
|
Uses `langgraph_sdk.Auth` with `@auth.authenticate` and `@auth.on` handlers.
|
|
697
702
|
If not set, all requests pass through as default_user (noop).
|
|
@@ -185,7 +185,7 @@ When the server starts it prints the banner and local URLs:
|
|
|
185
185
|
╠═╣│ ┬├┤ │││ │ ╚═╗├┤ ├┤ ├┴┐
|
|
186
186
|
╩ ╩└─┘└─┘┘└┘ ┴ ╚═╝└─┘└─┘┴ ┴
|
|
187
187
|
|
|
188
|
-
AgentSeek v0.2.
|
|
188
|
+
AgentSeek v0.2.3
|
|
189
189
|
|
|
190
190
|
- 🚀 API: http://localhost:2024
|
|
191
191
|
- 📚 Docs: http://localhost:2024/docs
|
|
@@ -659,7 +659,12 @@ parent api build --config ./langgraph.json -t my-api:dev
|
|
|
659
659
|
- `METADATA_DB_BACKEND=auto` normalizes drivers:
|
|
660
660
|
- PostgreSQL: `postgresql+asyncpg://...`
|
|
661
661
|
- OceanBase / MySQL: `mysql+aiomysql://...`
|
|
662
|
-
-
|
|
662
|
+
- SQLite: `sqlite+aiosqlite://...`
|
|
663
|
+
- Completed-run snapshots default to OceanBase / seekdb settings. With
|
|
664
|
+
`METADATA_DB_BACKEND=sqlite`, they are stored in the configured SQLite
|
|
665
|
+
metadata database without constructing an OceanBase saver.
|
|
666
|
+
- The SQLite metadata path still uses an in-memory LangGraph checkpointer; it
|
|
667
|
+
does not make LangGraph graph checkpoints durable.
|
|
663
668
|
- Auth: configure via `agentseek.json` `"auth.path"` or `AUTH_MODULE_PATH` env var.
|
|
664
669
|
Uses `langgraph_sdk.Auth` with `@auth.authenticate` and `@auth.on` handlers.
|
|
665
670
|
If not set, all requests pass through as default_user (noop).
|
|
@@ -181,7 +181,7 @@ uv run agentseek-api dev --config ./langgraph.json
|
|
|
181
181
|
╠═╣│ ┬├┤ │││ │ ╚═╗├┤ ├┤ ├┴┐
|
|
182
182
|
╩ ╩└─┘└─┘┘└┘ ┴ ╚═╝└─┘└─┘┴ ┴
|
|
183
183
|
|
|
184
|
-
AgentSeek v0.2.
|
|
184
|
+
AgentSeek v0.2.3
|
|
185
185
|
|
|
186
186
|
- 🚀 API: http://localhost:2024
|
|
187
187
|
- 📚 Docs: http://localhost:2024/docs
|
|
@@ -623,7 +623,12 @@ parent api build --config ./langgraph.json -t my-api:dev
|
|
|
623
623
|
- `METADATA_DB_BACKEND=auto` 会对驱动进行归一化:
|
|
624
624
|
- PostgreSQL:`postgresql+asyncpg://...`
|
|
625
625
|
- OceanBase / MySQL:`mysql+aiomysql://...`
|
|
626
|
-
-
|
|
626
|
+
- SQLite:`sqlite+aiosqlite://...`
|
|
627
|
+
- 完成态 run 快照默认使用 OceanBase / seekdb 配置。当
|
|
628
|
+
`METADATA_DB_BACKEND=sqlite` 时,快照会写入已配置的 SQLite 元数据
|
|
629
|
+
数据库,且不会构造 OceanBase saver。
|
|
630
|
+
- SQLite 元数据路径仍使用内存中的 LangGraph checkpointer,不提供持久化的
|
|
631
|
+
LangGraph 图 checkpoint。
|
|
627
632
|
- 鉴权:通过 `agentseek.json` 的 `"auth.path"` 或 `AUTH_MODULE_PATH` 环境变量配置。
|
|
628
633
|
使用 `langgraph_sdk.Auth` 的 `@auth.authenticate` 与 `@auth.on` 处理器风格。
|
|
629
634
|
未配置时所有请求以 default_user 放行(noop)。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.3"
|
|
@@ -3,7 +3,7 @@ import logging
|
|
|
3
3
|
import os
|
|
4
4
|
from collections.abc import Callable
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any, Protocol
|
|
7
7
|
|
|
8
8
|
from langgraph.checkpoint.memory import InMemorySaver
|
|
9
9
|
from langchain_oceanbase.checkpointer import OceanBaseCheckpointSaver as LangGraphOceanBaseCheckpointSaver
|
|
@@ -14,6 +14,7 @@ from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker
|
|
|
14
14
|
from agentseek_api.core.oceanbase_checkpointer import OceanBaseCheckpointSaver
|
|
15
15
|
from agentseek_api.core.orm import Base
|
|
16
16
|
from agentseek_api.core.runtime_store import SqliteStore
|
|
17
|
+
from agentseek_api.core.sqlite_checkpointer import SqliteCheckpointSaver
|
|
17
18
|
from agentseek_api.core.store_config import load_store_config
|
|
18
19
|
from agentseek_api.settings import settings
|
|
19
20
|
|
|
@@ -58,6 +59,18 @@ class NullStore:
|
|
|
58
59
|
return None
|
|
59
60
|
|
|
60
61
|
|
|
62
|
+
class RunCheckpointSaver(Protocol):
|
|
63
|
+
def setup(self) -> None: ...
|
|
64
|
+
|
|
65
|
+
def save_checkpoint(
|
|
66
|
+
self,
|
|
67
|
+
*,
|
|
68
|
+
thread_id: str,
|
|
69
|
+
run_id: str,
|
|
70
|
+
payload: dict[str, Any],
|
|
71
|
+
) -> None: ...
|
|
72
|
+
|
|
73
|
+
|
|
61
74
|
def _is_already_exists_error(exc: Exception) -> bool:
|
|
62
75
|
message = str(exc).lower()
|
|
63
76
|
return "already exists" in message
|
|
@@ -156,7 +169,7 @@ class DatabaseManager:
|
|
|
156
169
|
def __init__(self) -> None:
|
|
157
170
|
self.engine: AsyncEngine | None = None
|
|
158
171
|
self.session_factory: async_sessionmaker[AsyncSession] | None = None
|
|
159
|
-
self._checkpointer:
|
|
172
|
+
self._checkpointer: RunCheckpointSaver | None = None
|
|
160
173
|
self._langgraph_checkpointer: Any | None = None
|
|
161
174
|
self._store: Any | None = None
|
|
162
175
|
self._setup_lock: asyncio.Lock = asyncio.Lock()
|
|
@@ -208,11 +221,8 @@ class DatabaseManager:
|
|
|
208
221
|
async with self.engine.begin() as conn:
|
|
209
222
|
await conn.run_sync(Base.metadata.create_all)
|
|
210
223
|
await conn.run_sync(self._apply_additive_migrations)
|
|
211
|
-
conn_args = _resolve_connection_args()
|
|
212
|
-
self._checkpointer = OceanBaseCheckpointSaver(
|
|
213
|
-
connection_args=conn_args
|
|
214
|
-
)
|
|
215
224
|
if metadata_backend == "sqlite":
|
|
225
|
+
self._checkpointer = SqliteCheckpointSaver(url=metadata_db_url)
|
|
216
226
|
self._langgraph_checkpointer = InMemorySaver()
|
|
217
227
|
self._store = SqliteStore(
|
|
218
228
|
url=metadata_db_url,
|
|
@@ -220,6 +230,8 @@ class DatabaseManager:
|
|
|
220
230
|
ttl_config=runtime_ttl,
|
|
221
231
|
)
|
|
222
232
|
else:
|
|
233
|
+
conn_args = _resolve_connection_args()
|
|
234
|
+
self._checkpointer = OceanBaseCheckpointSaver(connection_args=conn_args)
|
|
223
235
|
self._langgraph_checkpointer = LangGraphOceanBaseCheckpointSaver(
|
|
224
236
|
connection_args=conn_args
|
|
225
237
|
)
|
|
@@ -320,6 +332,14 @@ class DatabaseManager:
|
|
|
320
332
|
self._store_setup_done = True
|
|
321
333
|
|
|
322
334
|
async def close(self) -> None:
|
|
335
|
+
run_checkpointer = self._checkpointer
|
|
336
|
+
self._checkpointer = None
|
|
337
|
+
close_run_checkpointer = getattr(run_checkpointer, "close", None)
|
|
338
|
+
if callable(close_run_checkpointer):
|
|
339
|
+
try:
|
|
340
|
+
await asyncio.to_thread(close_run_checkpointer)
|
|
341
|
+
except Exception: # noqa: BLE001
|
|
342
|
+
logger.warning("Failed to close run checkpointer", exc_info=True)
|
|
323
343
|
langgraph_checkpointer = self._langgraph_checkpointer
|
|
324
344
|
self._langgraph_checkpointer = None
|
|
325
345
|
close_checkpointer = getattr(langgraph_checkpointer, "close", None)
|
|
@@ -336,7 +356,6 @@ class DatabaseManager:
|
|
|
336
356
|
await asyncio.to_thread(dispose_store_engine)
|
|
337
357
|
self.engine = None
|
|
338
358
|
self.session_factory = None
|
|
339
|
-
self._checkpointer = None
|
|
340
359
|
self._store = None
|
|
341
360
|
self._checkpointer_setup_done = False
|
|
342
361
|
self._langgraph_checkpointer_setup_done = False
|
|
@@ -352,7 +371,7 @@ class DatabaseManager:
|
|
|
352
371
|
raise RuntimeError("Database not initialized")
|
|
353
372
|
return self.session_factory
|
|
354
373
|
|
|
355
|
-
def get_checkpointer(self) ->
|
|
374
|
+
def get_checkpointer(self) -> RunCheckpointSaver:
|
|
356
375
|
if self._checkpointer is None:
|
|
357
376
|
raise RuntimeError("Database not initialized")
|
|
358
377
|
return self._checkpointer
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import threading
|
|
4
|
+
from datetime import UTC, datetime
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import create_engine, text
|
|
8
|
+
from sqlalchemy.engine import Engine, make_url
|
|
9
|
+
from sqlalchemy.pool import StaticPool
|
|
10
|
+
|
|
11
|
+
from agentseek_api.services.sse import safe_json_dumps
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SqliteCheckpointSaver:
|
|
15
|
+
"""Persist completed run snapshots in the configured SQLite database."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, *, url: str) -> None:
|
|
18
|
+
parsed_url = make_url(url)
|
|
19
|
+
if parsed_url.get_backend_name() != "sqlite":
|
|
20
|
+
raise ValueError("SqliteCheckpointSaver requires a SQLite URL")
|
|
21
|
+
|
|
22
|
+
sqlite_url = parsed_url.set(drivername="sqlite").render_as_string(
|
|
23
|
+
hide_password=False
|
|
24
|
+
)
|
|
25
|
+
engine_options: dict[str, Any] = {"connect_args": {"check_same_thread": False}}
|
|
26
|
+
if parsed_url.database in {None, "", ":memory:"}:
|
|
27
|
+
engine_options["poolclass"] = StaticPool
|
|
28
|
+
|
|
29
|
+
self._engine: Engine = create_engine(sqlite_url, **engine_options)
|
|
30
|
+
self._lock = threading.Lock()
|
|
31
|
+
|
|
32
|
+
def setup(self) -> None:
|
|
33
|
+
with self._lock, self._engine.begin() as connection:
|
|
34
|
+
connection.execute(
|
|
35
|
+
text(
|
|
36
|
+
"""
|
|
37
|
+
CREATE TABLE IF NOT EXISTS agentseek_checkpoints (
|
|
38
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
39
|
+
thread_id TEXT NOT NULL,
|
|
40
|
+
run_id TEXT NOT NULL,
|
|
41
|
+
checkpoint TEXT NOT NULL,
|
|
42
|
+
created_at DATETIME NOT NULL
|
|
43
|
+
)
|
|
44
|
+
"""
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
connection.execute(
|
|
48
|
+
text(
|
|
49
|
+
"""
|
|
50
|
+
CREATE INDEX IF NOT EXISTS idx_agentseek_checkpoints_thread_run
|
|
51
|
+
ON agentseek_checkpoints (thread_id, run_id)
|
|
52
|
+
"""
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def save_checkpoint(
|
|
57
|
+
self,
|
|
58
|
+
*,
|
|
59
|
+
thread_id: str,
|
|
60
|
+
run_id: str,
|
|
61
|
+
payload: dict[str, Any],
|
|
62
|
+
) -> None:
|
|
63
|
+
with self._lock, self._engine.begin() as connection:
|
|
64
|
+
connection.execute(
|
|
65
|
+
text(
|
|
66
|
+
"""
|
|
67
|
+
INSERT INTO agentseek_checkpoints
|
|
68
|
+
(thread_id, run_id, checkpoint, created_at)
|
|
69
|
+
VALUES (:thread_id, :run_id, :checkpoint, :created_at)
|
|
70
|
+
"""
|
|
71
|
+
),
|
|
72
|
+
{
|
|
73
|
+
"thread_id": thread_id,
|
|
74
|
+
"run_id": run_id,
|
|
75
|
+
"checkpoint": safe_json_dumps(payload),
|
|
76
|
+
"created_at": datetime.now(UTC).replace(tzinfo=None).isoformat(),
|
|
77
|
+
},
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def close(self) -> None:
|
|
81
|
+
with self._lock:
|
|
82
|
+
self._engine.dispose()
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sqlite3
|
|
1
3
|
from collections.abc import Callable
|
|
4
|
+
from pathlib import Path
|
|
2
5
|
from typing import Any
|
|
3
6
|
|
|
4
7
|
import pytest
|
|
@@ -123,3 +126,53 @@ def test_health_uses_mysql_async_driver(monkeypatch: pytest.MonkeyPatch) -> None
|
|
|
123
126
|
|
|
124
127
|
def test_sqlite_metadata_engine_options_keep_dialect_defaults() -> None:
|
|
125
128
|
assert database_module._metadata_engine_options("sqlite") == {"pool_pre_ping": True}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_sqlite_runtime_starts_and_saves_checkpoints_without_oceanbase(
|
|
132
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
133
|
+
tmp_path: Path,
|
|
134
|
+
) -> None:
|
|
135
|
+
database_path = tmp_path / "agentseek.sqlite3"
|
|
136
|
+
|
|
137
|
+
class RejectOceanBaseCheckpointSaver:
|
|
138
|
+
def __init__(self, *_args: object, **_kwargs: object) -> None:
|
|
139
|
+
raise AssertionError(
|
|
140
|
+
"SQLite runtime constructed the OceanBase checkpoint saver"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
monkeypatch.setattr(
|
|
144
|
+
"agentseek_api.core.database.OceanBaseCheckpointSaver",
|
|
145
|
+
RejectOceanBaseCheckpointSaver,
|
|
146
|
+
)
|
|
147
|
+
monkeypatch.setattr(
|
|
148
|
+
"agentseek_api.main.ensure_default_assistants", _noop_ensure_default_assistants
|
|
149
|
+
)
|
|
150
|
+
monkeypatch.setattr(settings, "SEEKDB_EMBED", False)
|
|
151
|
+
monkeypatch.setattr(settings, "METADATA_DB_BACKEND", "sqlite")
|
|
152
|
+
monkeypatch.setattr(
|
|
153
|
+
settings,
|
|
154
|
+
"METADATA_DB_URL",
|
|
155
|
+
f"sqlite+aiosqlite:///{database_path.as_posix()}",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
manager = DatabaseManager()
|
|
159
|
+
monkeypatch.setattr("agentseek_api.main.db_manager", manager)
|
|
160
|
+
|
|
161
|
+
with TestClient(create_app()) as client:
|
|
162
|
+
response = client.get("/health")
|
|
163
|
+
assert response.status_code == 200
|
|
164
|
+
|
|
165
|
+
manager.get_checkpointer().save_checkpoint(
|
|
166
|
+
thread_id="thread-sqlite",
|
|
167
|
+
run_id="run-sqlite",
|
|
168
|
+
payload={"status": "complete"},
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
with sqlite3.connect(database_path) as connection:
|
|
172
|
+
row = connection.execute(
|
|
173
|
+
"SELECT thread_id, run_id, checkpoint FROM agentseek_checkpoints"
|
|
174
|
+
).fetchone()
|
|
175
|
+
|
|
176
|
+
assert row is not None
|
|
177
|
+
assert row[:2] == ("thread-sqlite", "run-sqlite")
|
|
178
|
+
assert json.loads(row[2]) == {"status": "complete"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_cli_compatibility_step_runs_sqlite_runtime_regressions_on_every_os() -> None:
|
|
6
|
+
workflow = Path(".github/workflows/ci.yml").read_text(encoding="utf-8")
|
|
7
|
+
job = re.search(
|
|
8
|
+
r"(?ms)^ cli-compatibility:\n(?P<body>.*?)(?=^ \S|\Z)",
|
|
9
|
+
workflow,
|
|
10
|
+
)
|
|
11
|
+
assert job is not None
|
|
12
|
+
|
|
13
|
+
step = re.search(
|
|
14
|
+
r"(?ms)^ - name: CLI config, host environment, and process tests\n"
|
|
15
|
+
r"(?P<body>.*?)(?=^ - name:|\Z)",
|
|
16
|
+
job.group("body"),
|
|
17
|
+
)
|
|
18
|
+
assert step is not None
|
|
19
|
+
|
|
20
|
+
command_tokens = set(step.group("body").split())
|
|
21
|
+
required_tests = {
|
|
22
|
+
"tests/unit/test_sqlite_checkpointer.py",
|
|
23
|
+
"tests/integration/test_metadata_db_config.py",
|
|
24
|
+
}
|
|
25
|
+
assert required_tests <= command_tokens
|
|
@@ -12,30 +12,26 @@ from agentseek_api.settings import settings
|
|
|
12
12
|
async def test_checkpointer_setup_called_once(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
13
13
|
class FakeCheckpointer:
|
|
14
14
|
setup_calls = 0
|
|
15
|
-
|
|
15
|
+
latest_url: str | None = None
|
|
16
16
|
|
|
17
|
-
def __init__(self,
|
|
18
|
-
FakeCheckpointer.
|
|
17
|
+
def __init__(self, *, url: str) -> None:
|
|
18
|
+
FakeCheckpointer.latest_url = url
|
|
19
19
|
|
|
20
20
|
def setup(self) -> None:
|
|
21
21
|
FakeCheckpointer.setup_calls += 1
|
|
22
22
|
|
|
23
|
-
monkeypatch.setattr(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
monkeypatch.setattr(settings, "
|
|
27
|
-
monkeypatch.setattr(settings, "
|
|
28
|
-
monkeypatch.setattr(settings, "OCEANBASE_PASSWORD", "")
|
|
29
|
-
monkeypatch.setattr(settings, "OCEANBASE_DB_NAME", "test")
|
|
23
|
+
monkeypatch.setattr(
|
|
24
|
+
"agentseek_api.core.database.SqliteCheckpointSaver", FakeCheckpointer
|
|
25
|
+
)
|
|
26
|
+
monkeypatch.setattr(settings, "METADATA_DB_URL", "sqlite+aiosqlite:///:memory:")
|
|
27
|
+
monkeypatch.setattr(settings, "METADATA_DB_BACKEND", "sqlite")
|
|
30
28
|
|
|
31
29
|
manager = DatabaseManager()
|
|
32
30
|
await manager.initialize()
|
|
33
31
|
await manager.initialize()
|
|
34
32
|
|
|
35
33
|
assert FakeCheckpointer.setup_calls == 1
|
|
36
|
-
assert FakeCheckpointer.
|
|
37
|
-
assert FakeCheckpointer.latest_connection_args["host"] == "127.0.0.1"
|
|
38
|
-
assert FakeCheckpointer.latest_connection_args["db_name"] == "test"
|
|
34
|
+
assert FakeCheckpointer.latest_url == "sqlite+aiosqlite:///:memory:"
|
|
39
35
|
assert isinstance(manager.get_store(), SqliteStore)
|
|
40
36
|
|
|
41
37
|
await manager.close()
|
|
@@ -60,6 +56,24 @@ async def test_close_cleans_up_langgraph_checkpointer() -> None:
|
|
|
60
56
|
engine.dispose.assert_awaited_once()
|
|
61
57
|
|
|
62
58
|
|
|
59
|
+
@pytest.mark.asyncio
|
|
60
|
+
async def test_close_cleans_up_run_checkpointer() -> None:
|
|
61
|
+
manager = DatabaseManager()
|
|
62
|
+
checkpointer = Mock()
|
|
63
|
+
close_thread_ids: list[int] = []
|
|
64
|
+
checkpointer.close.side_effect = lambda: close_thread_ids.append(
|
|
65
|
+
threading.get_ident()
|
|
66
|
+
)
|
|
67
|
+
manager._checkpointer = checkpointer
|
|
68
|
+
event_loop_thread_id = threading.get_ident()
|
|
69
|
+
|
|
70
|
+
await manager.close()
|
|
71
|
+
await manager.close()
|
|
72
|
+
|
|
73
|
+
checkpointer.close.assert_called_once()
|
|
74
|
+
assert close_thread_ids[0] != event_loop_thread_id
|
|
75
|
+
|
|
76
|
+
|
|
63
77
|
@pytest.mark.asyncio
|
|
64
78
|
async def test_close_continues_after_checkpointer_cleanup_fails(
|
|
65
79
|
caplog: pytest.LogCaptureFixture,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sqlite3
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from agentseek_api.core.sqlite_checkpointer import SqliteCheckpointSaver
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_setup_is_idempotent_and_save_checkpoint_persists_json(tmp_path: Path) -> None:
|
|
11
|
+
database_path = tmp_path / "checkpoints.sqlite3"
|
|
12
|
+
saver = SqliteCheckpointSaver(url=f"sqlite+aiosqlite:///{database_path.as_posix()}")
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
saver.setup()
|
|
16
|
+
saver.setup()
|
|
17
|
+
saver.save_checkpoint(
|
|
18
|
+
thread_id="thread-unit",
|
|
19
|
+
run_id="run-unit",
|
|
20
|
+
payload={"messages": ["hello"], "complete": True},
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
with sqlite3.connect(database_path) as connection:
|
|
24
|
+
row = connection.execute(
|
|
25
|
+
"SELECT thread_id, run_id, checkpoint FROM agentseek_checkpoints"
|
|
26
|
+
).fetchone()
|
|
27
|
+
indexes = connection.execute(
|
|
28
|
+
"PRAGMA index_list(agentseek_checkpoints)"
|
|
29
|
+
).fetchall()
|
|
30
|
+
finally:
|
|
31
|
+
saver.close()
|
|
32
|
+
|
|
33
|
+
assert row is not None
|
|
34
|
+
assert row[:2] == ("thread-unit", "run-unit")
|
|
35
|
+
assert json.loads(row[2]) == {"messages": ["hello"], "complete": True}
|
|
36
|
+
assert "idx_agentseek_checkpoints_thread_run" in {index[1] for index in indexes}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_rejects_non_sqlite_url() -> None:
|
|
40
|
+
with pytest.raises(ValueError, match="requires a SQLite URL"):
|
|
41
|
+
SqliteCheckpointSaver(url="mysql+aiomysql://root@localhost/agentseek")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_normalizes_windows_drive_path_without_opening_database() -> None:
|
|
45
|
+
saver = SqliteCheckpointSaver(url="sqlite+aiosqlite:///C:/agentseek/runtime.db")
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
assert saver._engine.url.drivername == "sqlite"
|
|
49
|
+
assert saver._engine.url.database == "C:/agentseek/runtime.db"
|
|
50
|
+
finally:
|
|
51
|
+
saver.close()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|