agentexec 0.2.0rc1__tar.gz → 0.2.0rc2__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.
- agentexec-0.2.0rc2/.python-version +1 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/CHANGELOG.md +113 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/PKG-INFO +2 -2
- agentexec-0.2.0rc2/RELEASE.md +91 -0
- agentexec-0.2.0rc2/examples/basic/debug_spawn.py +30 -0
- agentexec-0.2.0rc2/examples/basic/enqueue.py +26 -0
- agentexec-0.2.0rc2/examples/basic/entrypoint.sh +2 -0
- agentexec-0.2.0rc2/examples/basic/worker.py +50 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/pyproject.toml +9 -5
- agentexec-0.2.0rc2/research/multiprocessing.md +778 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/activity/__init__.py +76 -57
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/activity/events.py +9 -4
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/activity/handlers.py +28 -37
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/activity/models.py +106 -120
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/activity/producer.py +15 -24
- agentexec-0.2.0rc2/src/agentexec/cli.py +156 -0
- agentexec-0.2.0rc2/src/agentexec/core/db.py +65 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/core/queue.py +3 -3
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/core/results.py +2 -1
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/core/task.py +1 -1
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/schedule.py +3 -2
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/state/base.py +2 -1
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/state/kafka.py +8 -2
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/state/redis.py +25 -17
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/worker/pool.py +218 -169
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_activity_tracking.py +80 -90
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_db.py +10 -9
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_public_api.py +2 -2
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_queue_partitions.py +2 -1
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_runners.py +10 -11
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_schedule.py +4 -4
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_task.py +2 -2
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_task_locking.py +2 -2
- agentexec-0.2.0rc2/tests/test_worker_logging.py +439 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_worker_pool.py +47 -96
- agentexec-0.2.0rc1/src/agentexec/core/db.py +0 -52
- agentexec-0.2.0rc1/src/agentexec/core/logging.py +0 -27
- agentexec-0.2.0rc1/src/agentexec/worker/logging.py +0 -84
- agentexec-0.2.0rc1/tests/test_activity_tracking.py.bak +0 -427
- agentexec-0.2.0rc1/tests/test_worker_logging.py +0 -248
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/.claude/skills/prepare-release/SKILL.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/.github/workflows/ci.yml +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/.github/workflows/docker-publish.yml +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/.github/workflows/npm-publish.yml +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/.github/workflows/publish.yml +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/.gitignore +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/README.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docker/Dockerfile +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docker/README.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docker/entrypoint.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docker-compose.kafka.yml +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/api-reference/activity.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/api-reference/core.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/api-reference/pipeline.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/api-reference/runner.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/concepts/activity-tracking.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/concepts/architecture.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/concepts/task-lifecycle.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/concepts/worker-pool.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/contributing.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/deployment/docker.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/deployment/production.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/getting-started/configuration.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/getting-started/installation.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/getting-started/quickstart.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/guides/basic-usage.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/guides/custom-runners.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/guides/fastapi-integration.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/guides/openai-runner.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/guides/pipelines.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/docs/index.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/multi-tenancy/README.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/multi-tenancy/example.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/README.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/alembic/README +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/alembic/env.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/alembic/script.py.mako +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/alembic.ini +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/compose.yml +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/context.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/db.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/main.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/models.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/pipeline.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/pyproject.toml +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/tools.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/.gitignore +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/bun.lock +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/index.html +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/package.json +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/public/vite.svg +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/src/App.tsx +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/src/api/agents.ts +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/src/api/queries.ts +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/src/components/Layout.tsx +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/src/index.css +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/src/main.tsx +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/src/pages/AgentDetailPage.tsx +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/src/pages/AgentListPage.tsx +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/src/styles/github-dark.css +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/tsconfig.json +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/tsconfig.node.json +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/ui/vite.config.ts +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/views.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/openai-agents-fastapi/worker.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/queue-fairness/README.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/examples/queue-fairness/run.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/__init__.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/activity/schemas.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/activity/status.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/config.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/core/__init__.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/pipeline.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/runners/__init__.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/runners/base.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/runners/openai.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/state/__init__.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/tracker.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/worker/__init__.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/src/agentexec/worker/event.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_activity_schemas.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_config.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_kafka_integration.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_pipeline.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_pipeline_flow.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_queue.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_results.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_self_describing_results.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_state.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_state_backend.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_task_types.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/tests/test_worker_event.py +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/.gitignore +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/README.md +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/bun.lock +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/package.json +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/src/components/ActiveAgentsBadge.tsx +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/src/components/ProgressBar.tsx +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/src/components/StatusBadge.tsx +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/src/components/TaskDetail.tsx +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/src/components/TaskList.tsx +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/src/components/index.ts +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/src/index.ts +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/src/types.ts +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/tsconfig.json +0 -0
- {agentexec-0.2.0rc1 → agentexec-0.2.0rc2}/ui/vite.config.ts +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -1,5 +1,118 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.2.0rc2
|
|
4
|
+
|
|
5
|
+
Second release candidate for 0.2.0. Completes the async migration and
|
|
6
|
+
stabilizes the worker pool architecture.
|
|
7
|
+
|
|
8
|
+
### Breaking Changes
|
|
9
|
+
|
|
10
|
+
**Fully async database layer**
|
|
11
|
+
- `configure_engine()` and `get_session()` now require an async SQLAlchemy engine (`AsyncEngine`) and return `AsyncSession`
|
|
12
|
+
- Database URLs must use async drivers (e.g. `sqlite+aiosqlite://`, `postgresql+asyncpg://`)
|
|
13
|
+
- `sqlalchemy[asyncio]` is now a core dependency; `aiosqlite` added as dev dependency
|
|
14
|
+
|
|
15
|
+
**Async activity query API**
|
|
16
|
+
- `activity.list()`, `activity.detail()`, and `activity.count_active()` are now async and accept `AsyncSession`
|
|
17
|
+
- Activity handlers are now async (`async def __call__`)
|
|
18
|
+
|
|
19
|
+
**Removed `session` parameter from activity mutations**
|
|
20
|
+
- `activity.create()`, `activity.update()`, `activity.complete()`, and `activity.error()` no longer accept a `session` parameter — the handler owns its own session lifecycle
|
|
21
|
+
|
|
22
|
+
**Queue priority parameter**
|
|
23
|
+
- `BaseQueueBackend.push()` signature changed from `high_priority: bool` to `priority: Priority | None`
|
|
24
|
+
- Affects Redis, Kafka, and any custom queue backend implementations
|
|
25
|
+
|
|
26
|
+
### New Features
|
|
27
|
+
|
|
28
|
+
**CLI entrypoint**
|
|
29
|
+
- New `agentexec` CLI command via `[project.scripts]`
|
|
30
|
+
|
|
31
|
+
**Activity model `create()` classmethod**
|
|
32
|
+
- `Activity.create()` encapsulates record + initial log entry creation in one async call
|
|
33
|
+
|
|
34
|
+
**Async engine disposal**
|
|
35
|
+
- `dispose_engine()` ensures the async engine's background threads exit cleanly on shutdown
|
|
36
|
+
|
|
37
|
+
### Architecture Changes
|
|
38
|
+
|
|
39
|
+
**Worker pool refactor**
|
|
40
|
+
- Workers now use the `spawn` multiprocessing start method with explicit context — no inherited state
|
|
41
|
+
- `StateEvent` replaced with stdlib `multiprocessing.Event` — removes dependency on the state backend for shutdown coordination
|
|
42
|
+
- Event handling and scheduling extracted into `_EventHandler` and `_Scheduler` classes
|
|
43
|
+
- Worker processes are now daemonic with `SIGINT` ignored — clean shutdown driven by the event
|
|
44
|
+
- `pool.start()` handles `CancelledError` directly for Ctrl+C shutdown
|
|
45
|
+
|
|
46
|
+
**Logging overhaul**
|
|
47
|
+
- Removed `worker/logging.py` and `core/logging.py` — all modules use stdlib `logging.getLogger(__name__)`
|
|
48
|
+
- Spawned workers bootstrap a `StreamHandler` on the root logger so logs reach stderr
|
|
49
|
+
- Pool messages use `logger.info`/`logger.error` instead of `print()`
|
|
50
|
+
|
|
51
|
+
### Bug Fixes
|
|
52
|
+
|
|
53
|
+
- Fixed crash when worker receives a task for an unregistered task name (now logs error and skips)
|
|
54
|
+
- Failed tasks now log full tracebacks via `logger.exception` instead of `logger.error`
|
|
55
|
+
- Kafka consumer handles `None` message values without crashing
|
|
56
|
+
- `ActivityUpdated.status` is now `Status` enum instead of raw string
|
|
57
|
+
- `raise e` instead of bare `raise` in task execution for clearer tracebacks
|
|
58
|
+
|
|
59
|
+
### Improvements
|
|
60
|
+
|
|
61
|
+
- Dependency groups use PEP 735 `[dependency-groups]` instead of `[tool.uv]`
|
|
62
|
+
- Ruff line-length increased to 110
|
|
63
|
+
- Removed verbose docstring examples and redundant comments throughout activity models
|
|
64
|
+
- `selectinload` for eager loading of activity logs in `get_by_agent_id`
|
|
65
|
+
- Redis backend adds `type: ignore` annotations for redis-py async stubs
|
|
66
|
+
|
|
67
|
+
## v0.2.0rc1
|
|
68
|
+
|
|
69
|
+
First release candidate for 0.2.0. Major refactor of the backend, queue,
|
|
70
|
+
activity, and worker systems.
|
|
71
|
+
|
|
72
|
+
### Breaking Changes
|
|
73
|
+
|
|
74
|
+
**Backend module restructure**
|
|
75
|
+
- `agentexec.state.redis_backend` renamed to `agentexec.state.redis` — update `AGENTEXEC_STATE_BACKEND` if set explicitly
|
|
76
|
+
- `AGENTEXEC_QUEUE_NAME` renamed to `AGENTEXEC_QUEUE_PREFIX` (old name still accepted as alias)
|
|
77
|
+
|
|
78
|
+
**Async activity API**
|
|
79
|
+
- Activity functions are now async: `await ax.activity.create(...)`, `await ax.activity.update(...)`, etc.
|
|
80
|
+
|
|
81
|
+
**Task context serialization**
|
|
82
|
+
- `Task.context` is now `Mapping[str, Any]` (raw dict), not a typed BaseModel — hydration happens at execution time
|
|
83
|
+
- `Task.create()` is now async
|
|
84
|
+
|
|
85
|
+
**Removed APIs**
|
|
86
|
+
- `set_global_session`/`get_global_session`/`remove_global_session` — use `configure_engine`/`get_session`
|
|
87
|
+
- `state.backend.publish`/`subscribe` (pubsub), `index_add`/`index_range`/`index_remove`, `clear`, `configure`
|
|
88
|
+
|
|
89
|
+
### New Features
|
|
90
|
+
|
|
91
|
+
**Partitioned Redis queues**
|
|
92
|
+
- Tasks with `lock_key` route to dedicated partition queues with per-partition locking and SCAN-based fair dequeue
|
|
93
|
+
|
|
94
|
+
**Activity handler pattern**
|
|
95
|
+
- Pluggable persistence via `PostgresHandler` (default) and `IPCHandler` (worker processes)
|
|
96
|
+
|
|
97
|
+
**Task retry**
|
|
98
|
+
- Failed tasks requeue as high priority with `AGENTEXEC_MAX_TASK_RETRIES` (default 3)
|
|
99
|
+
|
|
100
|
+
**Kafka backend (experimental)**
|
|
101
|
+
- `pip install agentexec[kafka]` for queue and schedule via Kafka
|
|
102
|
+
|
|
103
|
+
**Typed worker IPC**
|
|
104
|
+
- `TaskFailed`, `LogEntry`, `ActivityUpdated` messages over `multiprocessing.Queue`
|
|
105
|
+
|
|
106
|
+
**Schedule composite keys**
|
|
107
|
+
- `{task_name}:{cron}:{context_hash}` for unique schedule identity
|
|
108
|
+
|
|
109
|
+
### Improvements
|
|
110
|
+
|
|
111
|
+
- Class-based backend architecture with ABCs (`BaseStateBackend`, `BaseQueueBackend`, `BaseScheduleBackend`)
|
|
112
|
+
- `Task` is pure data, `TaskDefinition` owns behavior
|
|
113
|
+
- Session management via `configure_engine`/`get_session` (Pool owns the engine)
|
|
114
|
+
- Status enum extracted to `activity/status.py` (no SQLAlchemy dependency)
|
|
115
|
+
|
|
3
116
|
## v0.1.7
|
|
4
117
|
|
|
5
118
|
### New Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentexec
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.0rc2
|
|
4
4
|
Summary: Production-ready orchestration for OpenAI Agents with Redis-backed coordination, activity tracking, and workflow management
|
|
5
5
|
Project-URL: Homepage, https://github.com/Agent-CI/agentexec
|
|
6
6
|
Project-URL: Documentation, https://github.com/Agent-CI/agentexec#readme
|
|
@@ -21,7 +21,7 @@ Requires-Dist: openai-agents>=0.1.0
|
|
|
21
21
|
Requires-Dist: pydantic-settings>=2.5.0
|
|
22
22
|
Requires-Dist: pydantic>=2.12.0
|
|
23
23
|
Requires-Dist: redis>=7.0.1
|
|
24
|
-
Requires-Dist: sqlalchemy>=2.0.44
|
|
24
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0.44
|
|
25
25
|
Provides-Extra: kafka
|
|
26
26
|
Requires-Dist: aiokafka>=0.11.0; extra == 'kafka'
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Release Process
|
|
2
|
+
|
|
3
|
+
This document describes how to publish a new release of agentexec.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Creating a **GitHub Release** triggers three publish workflows automatically:
|
|
8
|
+
|
|
9
|
+
| Workflow | Target | Secret Required |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `publish.yml` | PyPI | `PYPI_API_TOKEN` |
|
|
12
|
+
| `docker-publish.yml` | ghcr.io (`agentexec-worker`) | `GITHUB_TOKEN` (built-in) |
|
|
13
|
+
| `npm-publish.yml` | npm (`agentexec-ui`) | `NPM_TOKEN` |
|
|
14
|
+
|
|
15
|
+
Docker and npm publishing can also be triggered manually via workflow dispatch.
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
### 1. Ensure CI is green
|
|
20
|
+
|
|
21
|
+
Push all changes to `main` and verify the CI workflow passes (unit tests on
|
|
22
|
+
Python 3.12/3.13 and Kafka integration tests).
|
|
23
|
+
|
|
24
|
+
### 2. Bump the version
|
|
25
|
+
|
|
26
|
+
Update the version in `pyproject.toml`:
|
|
27
|
+
|
|
28
|
+
```toml
|
|
29
|
+
version = "X.Y.Z" # stable release
|
|
30
|
+
version = "X.Y.ZrcN" # release candidate
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
For the UI package, also update `ui/package.json`.
|
|
34
|
+
|
|
35
|
+
Commit and push:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git add pyproject.toml
|
|
39
|
+
git commit -m "Bump version to X.Y.Z"
|
|
40
|
+
git push
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 3. Update CHANGELOG.md
|
|
44
|
+
|
|
45
|
+
Add a new section at the top of `CHANGELOG.md` with release notes covering
|
|
46
|
+
breaking changes, new features, improvements, bug fixes, and testing updates.
|
|
47
|
+
|
|
48
|
+
### 4. Tag the release
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git tag vX.Y.Z
|
|
52
|
+
git push origin vX.Y.Z
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 5. Create the GitHub Release
|
|
56
|
+
|
|
57
|
+
This is what triggers the publish workflows.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
gh release create vX.Y.Z --title "vX.Y.Z" --notes-file CHANGELOG.md
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
For release candidates, mark as a pre-release:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
gh release create vX.Y.ZrcN --title "vX.Y.ZrcN" --generate-notes --prerelease
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 6. Verify
|
|
70
|
+
|
|
71
|
+
Check that all three publish workflows succeeded:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
gh run list --limit 5
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- **PyPI**: https://pypi.org/project/agentexec/
|
|
78
|
+
- **Docker**: https://ghcr.io/agent-ci/agentexec-worker
|
|
79
|
+
- **npm**: https://www.npmjs.com/package/agentexec-ui
|
|
80
|
+
|
|
81
|
+
## Manual Publishing
|
|
82
|
+
|
|
83
|
+
Docker and npm workflows support manual dispatch for one-off builds:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Docker with a custom tag
|
|
87
|
+
gh workflow run docker-publish.yml -f tag=dev
|
|
88
|
+
|
|
89
|
+
# npm with a version override
|
|
90
|
+
gh workflow run npm-publish.yml -f version=0.2.0-beta.1
|
|
91
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Debug: patch Worker to print tasks dict on startup, then run via Pool.run()."""
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
sys.path.insert(0, os.getcwd())
|
|
7
|
+
|
|
8
|
+
logging.basicConfig(
|
|
9
|
+
level=logging.INFO,
|
|
10
|
+
format="[%(levelname)s/%(processName)s] %(name)s: %(message)s",
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from worker import pool
|
|
14
|
+
from agentexec.worker.pool import Worker
|
|
15
|
+
|
|
16
|
+
_orig_init = Worker.__init__
|
|
17
|
+
|
|
18
|
+
def _debug_init(self, worker_id, context):
|
|
19
|
+
_orig_init(self, worker_id, context)
|
|
20
|
+
print(f"[DEBUG] Worker {worker_id} context.tasks = {list(context.tasks.keys())}", flush=True)
|
|
21
|
+
print(f"[DEBUG] Worker {worker_id} sys.path[:5] = {sys.path[:5]}", flush=True)
|
|
22
|
+
|
|
23
|
+
Worker.__init__ = _debug_init
|
|
24
|
+
|
|
25
|
+
from agentexec.config import CONF
|
|
26
|
+
CONF.num_workers = 1
|
|
27
|
+
|
|
28
|
+
if __name__ == "__main__":
|
|
29
|
+
print(f"[DEBUG] Parent tasks = {list(pool._context.tasks.keys())}")
|
|
30
|
+
pool.run()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Enqueue tasks for the worker pool.
|
|
2
|
+
|
|
3
|
+
Simulates what an API server would do when a user submits work.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
uv run python enqueue.py
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
|
|
11
|
+
import agentexec as ax
|
|
12
|
+
from worker import ResearchContext, GreetContext, ErrorContext
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async def main():
|
|
16
|
+
await ax.enqueue("research", ResearchContext(company="Anthropic"))
|
|
17
|
+
await ax.enqueue("research", ResearchContext(company="OpenAI"))
|
|
18
|
+
await ax.enqueue("greet", GreetContext(name="World"))
|
|
19
|
+
await ax.enqueue("error", ErrorContext())
|
|
20
|
+
|
|
21
|
+
print("Enqueued 4 tasks")
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Task definitions for the basic example.
|
|
2
|
+
|
|
3
|
+
This module is imported by both the run script and the spawned worker
|
|
4
|
+
processes. Keep it free of side effects — no asyncio.run(), no table
|
|
5
|
+
creation, no enqueue calls.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import logging
|
|
10
|
+
from uuid import UUID
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel
|
|
13
|
+
from sqlalchemy.ext.asyncio import create_async_engine
|
|
14
|
+
|
|
15
|
+
import agentexec as ax
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
engine = create_async_engine("sqlite+aiosqlite:///agents.db")
|
|
20
|
+
pool = ax.Pool(engine=engine)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ResearchContext(BaseModel):
|
|
24
|
+
company: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class GreetContext(BaseModel):
|
|
28
|
+
name: str
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ErrorContext(BaseModel):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pool.task("research")
|
|
36
|
+
async def research(agent_id: UUID, context: ResearchContext):
|
|
37
|
+
logger.info(f"Researching {context.company}...")
|
|
38
|
+
await asyncio.sleep(1)
|
|
39
|
+
logger.info(f"Done researching {context.company}")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pool.task("greet")
|
|
43
|
+
async def greet(agent_id: UUID, context: GreetContext):
|
|
44
|
+
logger.info(f"Hello, {context.name}!")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@pool.task("error")
|
|
48
|
+
async def handle_error(agent_id: UUID, context: ErrorContext) -> None:
|
|
49
|
+
logger.info("This will throw an error")
|
|
50
|
+
raise Exception("Intentional error")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "agentexec"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.0rc2"
|
|
4
4
|
description = "Production-ready orchestration for OpenAI Agents with Redis-backed coordination, activity tracking, and workflow management"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -23,7 +23,7 @@ dependencies = [
|
|
|
23
23
|
"redis>=7.0.1",
|
|
24
24
|
"pydantic>=2.12.0",
|
|
25
25
|
"pydantic-settings>=2.5.0",
|
|
26
|
-
"sqlalchemy>=2.0.44",
|
|
26
|
+
"sqlalchemy[asyncio]>=2.0.44",
|
|
27
27
|
"openai-agents>=0.1.0",
|
|
28
28
|
"croniter>=6.0.0",
|
|
29
29
|
]
|
|
@@ -34,6 +34,9 @@ kafka = [
|
|
|
34
34
|
]
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
[project.scripts]
|
|
38
|
+
agentexec = "agentexec.cli:main"
|
|
39
|
+
|
|
37
40
|
[project.urls]
|
|
38
41
|
Homepage = "https://github.com/Agent-CI/agentexec"
|
|
39
42
|
Documentation = "https://github.com/Agent-CI/agentexec#readme"
|
|
@@ -47,8 +50,8 @@ build-backend = "hatchling.build"
|
|
|
47
50
|
[tool.hatch.build.targets.wheel]
|
|
48
51
|
packages = ["src/agentexec"]
|
|
49
52
|
|
|
50
|
-
[
|
|
51
|
-
dev
|
|
53
|
+
[dependency-groups]
|
|
54
|
+
dev = [
|
|
52
55
|
"pytest>=8.0.0",
|
|
53
56
|
"pytest-asyncio>=0.23.0",
|
|
54
57
|
"pytest-cov>=4.1.0",
|
|
@@ -56,10 +59,11 @@ dev-dependencies = [
|
|
|
56
59
|
"ty>=0.0.1a7",
|
|
57
60
|
"fakeredis>=2.32.1",
|
|
58
61
|
"pytest-ty>=0.1.3",
|
|
62
|
+
"aiosqlite>=0.22.1",
|
|
59
63
|
]
|
|
60
64
|
|
|
61
65
|
[tool.ruff]
|
|
62
|
-
line-length =
|
|
66
|
+
line-length = 110
|
|
63
67
|
target-version = "py312"
|
|
64
68
|
|
|
65
69
|
[tool.ty.environment]
|