agentexec 0.1.2__tar.gz → 0.1.4__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.
Files changed (132) hide show
  1. agentexec-0.1.4/.claude/skills/prepare-release/SKILL.md +57 -0
  2. agentexec-0.1.4/.github/workflows/docker-publish.yml +65 -0
  3. agentexec-0.1.4/.github/workflows/npm-publish.yml +46 -0
  4. agentexec-0.1.4/CHANGELOG.md +211 -0
  5. {agentexec-0.1.2 → agentexec-0.1.4}/PKG-INFO +136 -43
  6. {agentexec-0.1.2 → agentexec-0.1.4}/README.md +132 -39
  7. agentexec-0.1.4/docker/Dockerfile +77 -0
  8. agentexec-0.1.4/docker/README.md +149 -0
  9. agentexec-0.1.4/docker/entrypoint.py +78 -0
  10. agentexec-0.1.4/docs/api-reference/activity.md +455 -0
  11. agentexec-0.1.4/docs/api-reference/core.md +476 -0
  12. agentexec-0.1.4/docs/api-reference/pipeline.md +529 -0
  13. agentexec-0.1.4/docs/api-reference/runner.md +430 -0
  14. agentexec-0.1.4/docs/concepts/activity-tracking.md +520 -0
  15. agentexec-0.1.4/docs/concepts/architecture.md +344 -0
  16. agentexec-0.1.4/docs/concepts/task-lifecycle.md +372 -0
  17. agentexec-0.1.4/docs/concepts/worker-pool.md +499 -0
  18. agentexec-0.1.4/docs/contributing.md +341 -0
  19. agentexec-0.1.4/docs/deployment/docker.md +557 -0
  20. agentexec-0.1.4/docs/deployment/production.md +597 -0
  21. agentexec-0.1.4/docs/getting-started/configuration.md +295 -0
  22. agentexec-0.1.4/docs/getting-started/installation.md +34 -0
  23. agentexec-0.1.4/docs/getting-started/quickstart.md +262 -0
  24. agentexec-0.1.4/docs/guides/basic-usage.md +437 -0
  25. agentexec-0.1.4/docs/guides/custom-runners.md +595 -0
  26. agentexec-0.1.4/docs/guides/fastapi-integration.md +301 -0
  27. agentexec-0.1.4/docs/guides/openai-runner.md +468 -0
  28. agentexec-0.1.4/docs/guides/pipelines.md +553 -0
  29. agentexec-0.1.4/docs/index.md +129 -0
  30. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/README.md +45 -1
  31. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/pipeline.py +10 -3
  32. agentexec-0.1.4/examples/openai-agents-fastapi/ui/.gitignore +3 -0
  33. agentexec-0.1.4/examples/openai-agents-fastapi/ui/bun.lock +422 -0
  34. agentexec-0.1.4/examples/openai-agents-fastapi/ui/index.html +13 -0
  35. agentexec-0.1.4/examples/openai-agents-fastapi/ui/package.json +27 -0
  36. agentexec-0.1.4/examples/openai-agents-fastapi/ui/public/vite.svg +4 -0
  37. agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/App.tsx +35 -0
  38. agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/api/agents.ts +37 -0
  39. agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/api/queries.ts +51 -0
  40. agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/components/Layout.tsx +38 -0
  41. agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/index.css +263 -0
  42. agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/main.tsx +10 -0
  43. agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/pages/AgentDetailPage.tsx +45 -0
  44. agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/pages/AgentListPage.tsx +68 -0
  45. agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/styles/github-dark.css +617 -0
  46. agentexec-0.1.4/examples/openai-agents-fastapi/ui/tsconfig.json +21 -0
  47. agentexec-0.1.4/examples/openai-agents-fastapi/ui/tsconfig.node.json +11 -0
  48. agentexec-0.1.4/examples/openai-agents-fastapi/ui/vite.config.ts +19 -0
  49. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/views.py +19 -0
  50. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/worker.py +4 -1
  51. {agentexec-0.1.2 → agentexec-0.1.4}/pyproject.toml +11 -12
  52. agentexec-0.1.4/src/agentexec/__init__.py +64 -0
  53. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/activity/__init__.py +6 -17
  54. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/activity/models.py +46 -7
  55. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/activity/schemas.py +2 -2
  56. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/activity/tracker.py +23 -12
  57. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/config.py +12 -0
  58. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/core/queue.py +13 -16
  59. agentexec-0.1.4/src/agentexec/core/results.py +64 -0
  60. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/core/task.py +91 -47
  61. agentexec-0.1.4/src/agentexec/pipeline.py +454 -0
  62. agentexec-0.1.4/src/agentexec/runners/__init__.py +5 -0
  63. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/runners/base.py +14 -10
  64. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/runners/openai.py +10 -13
  65. agentexec-0.1.4/src/agentexec/state/__init__.py +216 -0
  66. agentexec-0.1.4/src/agentexec/state/backend.py +275 -0
  67. agentexec-0.1.4/src/agentexec/state/redis_backend.py +380 -0
  68. agentexec-0.1.4/src/agentexec/tracker.py +67 -0
  69. agentexec-0.1.4/src/agentexec/worker/__init__.py +5 -0
  70. agentexec-0.1.4/src/agentexec/worker/event.py +48 -0
  71. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/worker/logging.py +10 -11
  72. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/worker/pool.py +62 -50
  73. agentexec-0.1.4/tests/test_activity_schemas.py +224 -0
  74. agentexec-0.1.4/tests/test_activity_tracking.py +355 -0
  75. agentexec-0.1.4/tests/test_config.py +204 -0
  76. agentexec-0.1.4/tests/test_db.py +134 -0
  77. agentexec-0.1.4/tests/test_pipeline.py +172 -0
  78. agentexec-0.1.4/tests/test_pipeline_flow.py +437 -0
  79. {agentexec-0.1.2 → agentexec-0.1.4}/tests/test_public_api.py +20 -29
  80. agentexec-0.1.4/tests/test_queue.py +209 -0
  81. agentexec-0.1.4/tests/test_results.py +193 -0
  82. agentexec-0.1.4/tests/test_runners.py +565 -0
  83. agentexec-0.1.4/tests/test_self_describing_results.py +126 -0
  84. agentexec-0.1.4/tests/test_state.py +185 -0
  85. agentexec-0.1.4/tests/test_state_backend.py +292 -0
  86. agentexec-0.1.4/tests/test_task.py +319 -0
  87. agentexec-0.1.4/tests/test_worker_event.py +133 -0
  88. agentexec-0.1.4/tests/test_worker_logging.py +280 -0
  89. agentexec-0.1.4/tests/test_worker_pool.py +247 -0
  90. agentexec-0.1.4/ui/.gitignore +3 -0
  91. agentexec-0.1.4/ui/README.md +148 -0
  92. agentexec-0.1.4/ui/bun.lock +397 -0
  93. agentexec-0.1.4/ui/package.json +52 -0
  94. agentexec-0.1.4/ui/src/components/ActiveAgentsBadge.tsx +20 -0
  95. agentexec-0.1.4/ui/src/components/ProgressBar.tsx +24 -0
  96. agentexec-0.1.4/ui/src/components/StatusBadge.tsx +42 -0
  97. agentexec-0.1.4/ui/src/components/TaskDetail.tsx +121 -0
  98. agentexec-0.1.4/ui/src/components/TaskList.tsx +119 -0
  99. agentexec-0.1.4/ui/src/components/index.ts +5 -0
  100. agentexec-0.1.4/ui/src/index.ts +23 -0
  101. agentexec-0.1.4/ui/src/types.ts +59 -0
  102. agentexec-0.1.4/ui/tsconfig.json +24 -0
  103. agentexec-0.1.4/ui/vite.config.ts +34 -0
  104. agentexec-0.1.2/CHANGELOG.md +0 -82
  105. agentexec-0.1.2/src/agentexec/__init__.py +0 -72
  106. agentexec-0.1.2/src/agentexec/core/redis_client.py +0 -71
  107. agentexec-0.1.2/src/agentexec/core/results.py +0 -62
  108. agentexec-0.1.2/src/agentexec/pipeline.py +0 -199
  109. agentexec-0.1.2/src/agentexec/runners/__init__.py +0 -13
  110. agentexec-0.1.2/src/agentexec/worker/__init__.py +0 -7
  111. agentexec-0.1.2/src/agentexec/worker/event.py +0 -43
  112. agentexec-0.1.2/tests/test_activity_tracking.py +0 -89
  113. agentexec-0.1.2/tests/test_redis_client.py +0 -63
  114. agentexec-0.1.2/tests/test_task.py +0 -169
  115. agentexec-0.1.2/tests/test_worker_pool.py +0 -145
  116. {agentexec-0.1.2 → agentexec-0.1.4}/.github/workflows/publish.yml +0 -0
  117. {agentexec-0.1.2 → agentexec-0.1.4}/.gitignore +0 -0
  118. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/alembic/README +0 -0
  119. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/alembic/env.py +0 -0
  120. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/alembic/script.py.mako +0 -0
  121. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/alembic.ini +0 -0
  122. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/compose.yml +0 -0
  123. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/context.py +0 -0
  124. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/db.py +0 -0
  125. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/main.py +0 -0
  126. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/models.py +0 -0
  127. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/pyproject.toml +0 -0
  128. {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/tools.py +0 -0
  129. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/core/__init__.py +0 -0
  130. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/core/db.py +0 -0
  131. {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/core/logging.py +0 -0
  132. {agentexec-0.1.2 → agentexec-0.1.4}/tests/test_activity_tracking.py.bak +0 -0
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: prepare-release
3
+ description: Prepare a new release by updating CHANGELOG.md with changes since the last tag and bumping version numbers in pyproject.toml and ui/package.json
4
+ ---
5
+
6
+ # Prepare Release
7
+
8
+ When preparing a release, follow these steps:
9
+
10
+ ## 1. Fetch tags and identify the last release
11
+
12
+ ```bash
13
+ git fetch --tags
14
+ git tag --sort=-creatordate | head -5
15
+ ```
16
+
17
+ ## 2. Get commits since the last release
18
+
19
+ ```bash
20
+ git log --oneline <last-tag>..HEAD
21
+ ```
22
+
23
+ ## 3. Read current files
24
+
25
+ Read these files to understand current state:
26
+ - `CHANGELOG.md` - to understand the format and existing entries
27
+ - `pyproject.toml` - to get current version
28
+ - `ui/package.json` - to get current UI version
29
+
30
+ ## 4. Update CHANGELOG.md
31
+
32
+ - Add a new version section at the top (below the `# Changelog` header)
33
+ - Organize changes into categories:
34
+ - **Breaking Changes** - API changes that require user action
35
+ - **New Features** - New functionality
36
+ - **Internal Improvements** - Refactoring, tests, tooling
37
+ - Use bold headers for each change group
38
+ - Use bullet points for details
39
+ - If there was an "Unreleased" section, rename it to the previous version
40
+
41
+ ## 5. Bump versions
42
+
43
+ Update the version string in:
44
+ - `pyproject.toml` - the `version` field under `[project]`
45
+ - `ui/package.json` - the `"version"` field
46
+
47
+ Use semantic versioning:
48
+ - Patch (0.0.x) for bug fixes
49
+ - Minor (0.x.0) for new features
50
+ - Major (x.0.0) for breaking changes
51
+
52
+ ## 6. Summary
53
+
54
+ After completing, summarize what was updated:
55
+ - New version number
56
+ - Key changes added to changelog
57
+ - Files modified
@@ -0,0 +1,65 @@
1
+ name: Publish Docker Image
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ # Allow manual trigger for testing
7
+ workflow_dispatch:
8
+ inputs:
9
+ tag:
10
+ description: 'Image tag (defaults to "dev")'
11
+ required: false
12
+ default: 'dev'
13
+
14
+ env:
15
+ REGISTRY: ghcr.io
16
+ IMAGE_NAME: agent-ci/agentexec-worker
17
+
18
+ permissions:
19
+ contents: read
20
+ packages: write
21
+
22
+ jobs:
23
+ build-and-push:
24
+ runs-on: ubuntu-latest
25
+
26
+ steps:
27
+ - name: Checkout repository
28
+ uses: actions/checkout@v4
29
+
30
+ - name: Set up Docker Buildx
31
+ uses: docker/setup-buildx-action@v3
32
+
33
+ - name: Log in to Container Registry
34
+ uses: docker/login-action@v3
35
+ with:
36
+ registry: ${{ env.REGISTRY }}
37
+ username: ${{ github.actor }}
38
+ password: ${{ secrets.GITHUB_TOKEN }}
39
+
40
+ - name: Extract metadata for Docker
41
+ id: meta
42
+ uses: docker/metadata-action@v5
43
+ with:
44
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
45
+ tags: |
46
+ # Tag with version on release (e.g., v0.1.2 -> 0.1.2)
47
+ type=semver,pattern={{version}}
48
+ # Tag with major.minor on release (e.g., v0.1.2 -> 0.1)
49
+ type=semver,pattern={{major}}.{{minor}}
50
+ # Tag as "latest" on release
51
+ type=raw,value=latest,enable=${{ github.event_name == 'release' }}
52
+ # Tag with input value on manual dispatch
53
+ type=raw,value=${{ inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' }}
54
+
55
+ - name: Build and push Docker image
56
+ uses: docker/build-push-action@v5
57
+ with:
58
+ context: ./docker
59
+ file: ./docker/Dockerfile
60
+ push: true
61
+ tags: ${{ steps.meta.outputs.tags }}
62
+ labels: ${{ steps.meta.outputs.labels }}
63
+ cache-from: type=gha
64
+ cache-to: type=gha,mode=max
65
+ platforms: linux/amd64,linux/arm64
@@ -0,0 +1,46 @@
1
+ name: Publish agentexec-ui to npm
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ inputs:
8
+ version:
9
+ description: 'Version to publish (leave empty to use package.json version)'
10
+ required: false
11
+ type: string
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ publish:
18
+ runs-on: ubuntu-latest
19
+
20
+ defaults:
21
+ run:
22
+ working-directory: ui
23
+
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+
27
+ - name: Setup Node.js
28
+ uses: actions/setup-node@v4
29
+ with:
30
+ node-version: '20'
31
+ registry-url: 'https://registry.npmjs.org'
32
+
33
+ - name: Install dependencies
34
+ run: npm install
35
+
36
+ - name: Update version (if specified)
37
+ if: inputs.version != ''
38
+ run: npm version ${{ inputs.version }} --no-git-tag-version
39
+
40
+ - name: Build package
41
+ run: npm run build
42
+
43
+ - name: Publish to npm
44
+ run: npm publish --access public
45
+ env:
46
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,211 @@
1
+ # Changelog
2
+
3
+ ## v0.1.4
4
+
5
+ ### Breaking Changes
6
+
7
+ **Renamed `WorkerPool` to `Pool`**
8
+ - `ax.WorkerPool` is now `ax.Pool` for cleaner API
9
+ - Update imports: `from agentexec import Pool`
10
+
11
+ **Activity percentage field renamed**
12
+ - `completion_%` renamed to `percentage` for cleaner field naming
13
+
14
+ ### New Features
15
+
16
+ **Pipelines run on workers**
17
+ - Pipelines can now be executed on worker processes
18
+ - Register pipelines with the pool and enqueue them like tasks
19
+
20
+ **Tracker for stateful counters**
21
+ - New `Tracker` class for managing stateful counters across workers
22
+ - Useful for tracking progress, metrics, and distributed state
23
+
24
+ **Strict Pipeline type flow validation**
25
+ - All step parameters and return types must be `BaseModel` subclasses
26
+ - Type flow between consecutive steps is validated at runtime
27
+ - Tuple returns are unpacked and matched to next step's parameters
28
+ - Final step must return a single `BaseModel` (not a tuple)
29
+ - Empty pipelines raise `RuntimeError` at class definition time
30
+
31
+ ### Internal Improvements
32
+
33
+ **Type checking with `ty`**
34
+ - Added `ty` type checker to development workflow
35
+ - Better Protocol definitions for step handlers
36
+ - Improved type hints throughout pipeline module
37
+
38
+ **Better Pipeline flow tests**
39
+ - Comprehensive test coverage for valid and invalid type flows
40
+ - Tests for tuple unpacking, subclass compatibility, count mismatches
41
+ - Tests for primitive type rejection and edge cases
42
+
43
+ ## v0.1.3
44
+
45
+ ### Breaking Changes
46
+
47
+ **Self-describing JSON serialization replaces pickle**
48
+ - Task results now use JSON serialization with embedded type information (similar to pickle)
49
+ - Automatically stores fully qualified class name with data for type reconstruction
50
+ - No longer requires `TaskDefinition` registry for result deserialization
51
+ - `ax.gather()` now works with tasks created via `ax.enqueue()` without pool context
52
+ - **Migration**: Clear Redis or wait for TTL expiry on old pickled results
53
+
54
+ **TaskHandler Protocol enforces BaseModel returns**
55
+ - Task handlers must return a Pydantic `BaseModel` instance (not `None` or arbitrary objects)
56
+ - Return type is automatically inferred and validated at registration time
57
+ - Enables type-safe result retrieval and automatic serialization
58
+
59
+ ### New Features
60
+
61
+ **State backend abstraction**
62
+ - Introduced `StateBackend` Protocol for pluggable state storage implementations
63
+ - Current Redis implementation moved to `agentexec.state.redis_backend`
64
+ - Backend modules verified against protocol at import time via `cast()`
65
+ - Prepares foundation for alternative backends (in-memory, DynamoDB, etc.)
66
+
67
+ **Improved async patterns**
68
+ - `brpop()` is now a proper async function (was sync returning coroutine)
69
+ - Consistent async/await usage across state operations
70
+ - Better type hints and IDE support
71
+
72
+ **Enhanced type safety**
73
+ - `TaskHandler` Protocol with support for both sync and async handlers
74
+ - Proper type annotations for all state backend operations
75
+ - `serialize()` and `deserialize()` type-enforced for `BaseModel` only
76
+
77
+ ### Documentation
78
+
79
+ **Comprehensive documentation added**
80
+ - API reference for core modules (activity, pipeline, runner, task)
81
+ - Conceptual guides (architecture, task lifecycle, worker pool)
82
+ - Deployment guides (Docker, production best practices)
83
+ - Usage guides (basic usage, pipelines, FastAPI integration, OpenAI runner)
84
+ - Getting started (installation, quickstart, configuration)
85
+ - Contributing guide
86
+
87
+ ### UI & Tooling
88
+
89
+ **React frontend and component library**
90
+ - Added `agentexec-ui` npm package with reusable React components
91
+ - Pre-built UI for agent monitoring and activity tracking
92
+ - TanStack Query integration for real-time updates
93
+ - React Router for navigation between agent list and detail views
94
+
95
+ **Docker deployment**
96
+ - Docker worker image for containerized deployments
97
+ - GitHub Actions for automated Docker image publishing to GitHub Container Registry
98
+ - GitHub Actions for automated npm publishing of UI components
99
+
100
+ ### Testing
101
+
102
+ **Comprehensive test coverage**
103
+ - Achieved 89% code coverage
104
+ - Added unit tests for all core modules:
105
+ - State backend and serialization (`test_state.py`, `test_state_backend.py`)
106
+ - Self-describing results (`test_self_describing_results.py`)
107
+ - Activity tracking schemas (`test_activity_schemas.py`)
108
+ - Pipeline orchestration (`test_pipeline.py`)
109
+ - Task queue operations (`test_queue.py`)
110
+ - Worker events and logging (`test_worker_event.py`, `test_worker_logging.py`)
111
+ - Database operations (`test_db.py`)
112
+ - Configuration (`test_config.py`)
113
+
114
+ ### Internal Improvements
115
+
116
+ **Redis client refactoring**
117
+ - Removed `core/redis_client.py` in favor of state backend abstraction
118
+ - Lazy connection initialization for both async and sync Redis clients
119
+ - Proper connection cleanup in `backend.close()`
120
+
121
+ **Key formatting consistency**
122
+ - All state keys use consistent `agentexec:` prefix via `backend.format_key()`
123
+ - Results: `agentexec:result:{agent_id}`
124
+ - Events: `agentexec:event:{name}:{id}`
125
+ - Logs channel: `agentexec:logs`
126
+
127
+ **Standardized function signatures**
128
+ - `get_result()` and `gather()` return `BaseModel` directly (not JSON strings)
129
+ - Consistent parameter ordering across state module functions
130
+ - Better docstrings with type information
131
+
132
+ ## v0.1.2
133
+
134
+ ### New Features
135
+
136
+ **Pipelines**
137
+ - Multi-step workflow orchestration with `ax.Pipeline`
138
+ - Define steps with `@pipeline.step(order)` decorator
139
+ - Parallel task execution with `ax.gather()`
140
+ - Result retrieval with `ax.get_result()`
141
+
142
+ **Worker logging via Redis pubsub**
143
+ - Workers publish logs to Redis, collected by main process
144
+ - Use `pool.run()` to see worker logs in real-time
145
+
146
+ ### Internal Improvements
147
+
148
+ **Reorganized worker module**
149
+ - Worker code moved to `agentexec.worker` subpackage
150
+ - `RedisEvent` for cross-process shutdown coordination
151
+ - `get_worker_logger()` configures logging and returns logger in one call
152
+
153
+ **Refactored Redis client usage**
154
+ - Added `get_redis_sync()` for synchronous Redis operations
155
+ - Sync/async Redis clients for different contexts
156
+
157
+ ## v0.1.1
158
+
159
+ ### Breaking Changes
160
+
161
+ **Async `enqueue()` function**
162
+ - `ax.enqueue()` is now async and must be awaited:
163
+ ```python
164
+ task = await ax.enqueue("task_name", MyContext(key="value"))
165
+ ```
166
+
167
+ **Type-safe context with Pydantic BaseModel**
168
+ - Task context must be a Pydantic `BaseModel` instead of a raw `dict`
169
+ - Context class is automatically inferred from handler type hints:
170
+ ```python
171
+ class ResearchContext(BaseModel):
172
+ company: str
173
+
174
+ @pool.task("research")
175
+ async def research(agent_id: UUID, context: ResearchContext):
176
+ company = context.company # Type-safe with IDE autocomplete
177
+ ```
178
+
179
+ **Redis URL now required**
180
+ - `redis_url` defaults to `None` and must be explicitly configured via `REDIS_URL`
181
+ - Prevents accidental connections to wrong Redis instances
182
+
183
+ ### New Features
184
+
185
+ **Configurable activity messages**
186
+ - Activity status messages are configurable via environment variables:
187
+ ```bash
188
+ AGENTEXEC_ACTIVITY_MESSAGE_CREATE="Waiting to start."
189
+ AGENTEXEC_ACTIVITY_MESSAGE_STARTED="Task started."
190
+ AGENTEXEC_ACTIVITY_MESSAGE_COMPLETE="Task completed successfully."
191
+ AGENTEXEC_ACTIVITY_MESSAGE_ERROR="Task failed with error: {error}"
192
+ ```
193
+
194
+ **Improved Task architecture**
195
+ - `Task` is now the primary execution object with `execute()` method
196
+ - `TaskDefinition` handles registration metadata and context class inference
197
+ - Full lifecycle management (QUEUED → RUNNING → COMPLETE/ERROR) encapsulated in `Task.execute()`
198
+
199
+ **Better SQLAlchemy session management**
200
+ - New `scoped_session` pattern for worker processes
201
+ - Proper session cleanup on worker shutdown
202
+
203
+ ### Internal Improvements
204
+
205
+ - Switched to async Redis client (`redis.asyncio`)
206
+ - Consolidated cleanup code in worker `_run()` method
207
+ - Removed unused `debug` config option
208
+
209
+ ## v0.1.0
210
+
211
+ Initial release.
@@ -1,21 +1,21 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentexec
3
- Version: 0.1.2
3
+ Version: 0.1.4
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
7
7
  Project-URL: Repository, https://github.com/Agent-CI/agentexec
8
8
  Project-URL: Issues, https://github.com/Agent-CI/agentexec/issues
9
- Author-email: Agent CI <hello@agentci.com>, Travis Dent <root@a10k.co>
9
+ Author-email: Agent CI <hello@agent-ci.com>, Travis Dent <root@a10k.co>
10
10
  License: MIT
11
11
  Keywords: agents,async,background-workers,openai,orchestration,redis
12
12
  Classifier: Development Status :: 3 - Alpha
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: License :: OSI Approved :: MIT License
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.11
17
16
  Classifier: Programming Language :: Python :: 3.12
18
- Requires-Python: >=3.11
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Requires-Python: >=3.12
19
19
  Requires-Dist: openai-agents>=0.1.0
20
20
  Requires-Dist: pydantic-settings>=2.5.0
21
21
  Requires-Dist: pydantic>=2.12.0
@@ -25,6 +25,12 @@ Description-Content-Type: text/markdown
25
25
 
26
26
  # `agentexec`
27
27
 
28
+ [![PyPI](https://img.shields.io/pypi/v/agentexec?color=blue)](https://pypi.org/project/agentexec/)
29
+ [![Python](https://img.shields.io/badge/python-3.12+-blue)](https://www.python.org/)
30
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
31
+ [![Type Checked](https://img.shields.io/badge/type%20checked-ty-blue)](https://github.com/astral-sh/ty)
32
+ [![Code Style](https://img.shields.io/badge/code%20style-ruff-orange)](https://github.com/astral-sh/ruff)
33
+
28
34
  **Production-ready orchestration for OpenAI Agents SDK** with Redis-backed task queues, SQLAlchemy activity tracking, and multiprocessing worker pools.
29
35
 
30
36
  Build reliable, scalable AI agent applications with automatic lifecycle management, progress tracking, and fault tolerance.
@@ -61,7 +67,7 @@ uv add agentexec
61
67
  ```
62
68
 
63
69
  **Requirements:**
64
- - Python 3.11+
70
+ - Python 3.12+
65
71
  - Redis (for task queue)
66
72
  - SQLAlchemy-compatible database (for activity tracking)
67
73
  - Agents that you want to parallelize!
@@ -82,16 +88,16 @@ from sqlalchemy import create_engine
82
88
  import agentexec as ax
83
89
 
84
90
 
85
- # Define typed context for your task
91
+ # agentexec uses Pydantic models for input and outuput schemas
86
92
  class ResearchContext(BaseModel):
87
93
  company: str
88
94
 
89
95
 
90
- # Database for activity tracking (share with your app)
96
+ # Database for activity tracking (share with the rest of your app)
91
97
  engine = create_engine("sqlite:///agents.db")
92
98
 
93
99
  # Create worker pool
94
- pool = ax.WorkerPool(engine=engine)
100
+ pool = ax.Pool(engine=engine)
95
101
 
96
102
 
97
103
  @pool.task("research_company")
@@ -99,45 +105,34 @@ async def research_company(agent_id: UUID, context: ResearchContext) -> None:
99
105
  """Background task that runs an AI agent."""
100
106
  runner = ax.OpenAIRunner(
101
107
  agent_id=agent_id,
102
- max_turns_recovery=True,
103
108
  )
104
109
 
105
110
  agent = Agent(
106
111
  name="Research Agent",
107
112
  instructions=(
108
- f"Research {context.company}.\n" # Typed access!
109
- "\n"
110
- f"{runner.prompts.report_status}"
113
+ f"Research {context.company}.\n"
114
+ runner.prompts.report_status
111
115
  ),
112
116
  tools=[
113
- runner.tools.report_status,
117
+ runner.tools.report_status, # Automatically associated with agent_id
114
118
  ],
115
- model="gpt-4o",
119
+ model="gpt-5",
116
120
  )
117
121
 
118
122
  result = await runner.run(
119
123
  agent,
120
124
  input="Start research",
121
- max_turns=15,
122
125
  )
123
- print(f"Done! {result.final_output}")
126
+ print(f"Done! {result.final_output}") # Native result object
124
127
 
125
128
 
126
129
  if __name__ == "__main__":
127
- pool.start() # Start workers
130
+ pool.start() # Start worker process
128
131
  ```
129
132
 
130
133
  ### 2. Queue Tasks from Your Application
131
134
 
132
135
  ```python
133
- import agentexec as ax
134
- from pydantic import BaseModel
135
-
136
-
137
- class ResearchContext(BaseModel):
138
- company: str
139
-
140
-
141
136
  # Enqueue a task (from your async API handler, etc.)
142
137
  task = await ax.enqueue(
143
138
  "research_company",
@@ -186,12 +181,19 @@ Agents can report their own progress using a built-in tool:
186
181
  ```python
187
182
  agent = Agent(
188
183
  instructions=f"Do research. {runner.prompts.report_status}",
189
- tools=[runner.tools.report_status], # Agent can call this
184
+ tools=[runner.tools.report_status], # Agent passes a short message and percentage
190
185
  )
191
186
 
192
187
  # Agent will report: "Gathering data" (40%), "Analyzing results" (80%), etc.
193
188
  ```
194
189
 
190
+ ### Explicit Reporting
191
+ Manually update activity status and progress from within your task:
192
+
193
+ ```python
194
+ ax.activity.update(agent_id, "Starting data collection", percentage=10)
195
+ ```
196
+
195
197
  ### Max Turns Recovery
196
198
 
197
199
  Automatically handle conversation limits with graceful wrap-up:
@@ -202,6 +204,10 @@ runner = ax.OpenAIRunner(
202
204
  max_turns_recovery=True,
203
205
  wrap_up_prompt="Please summarize your findings.",
204
206
  )
207
+ agent = Agent(
208
+ instructions="Research the topic thoroughly."
209
+ )
210
+ result = await runner.run(agent, max_turns=5)
205
211
 
206
212
  # If agent hits max turns, runner automatically:
207
213
  # 1. Catches MaxTurnsExceeded
@@ -226,32 +232,76 @@ await ax.enqueue("batch_job", context, priority=ax.Priority.LOW)
226
232
  Orchestrate multi-step workflows with parallel task execution:
227
233
 
228
234
  ```python
229
- import agentexec as ax
230
-
231
- pipeline = ax.Pipeline(pool=pool)
235
+ pipeline = ax.Pipeline(pool)
232
236
 
233
237
 
234
238
  class MyPipeline(pipeline.Base):
235
- @pipeline.step(0)
236
- async def parallel_research(self, input_ctx: InputContext):
239
+ @pipeline.step(0, "brand and market research")
240
+ async def parallel_research(self, context: InputContext):
237
241
  """Run multiple tasks in parallel."""
238
- task1 = await ax.enqueue("research_brand", BrandContext(...))
239
- task2 = await ax.enqueue("research_market", MarketContext(...))
240
- return await ax.gather(task1, task2)
242
+ brand_task = await ax.enqueue("research_brand", context)
243
+ market_task = await ax.enqueue("research_market", context)
244
+ return await ax.gather(brand_task, market_task)
241
245
 
242
- @pipeline.step(1)
243
- async def analyze(self, brand_result, market_result):
246
+ @pipeline.step(1, "research analysis")
247
+ async def analyze(self, brand_result: BrandResult, market_result: MarketResult) -> AnalysisResult:
244
248
  """Combine results from previous step."""
245
- task = await ax.enqueue("analyze", AnalysisContext(...))
246
- return await ax.get_result(task.agent_id)
249
+ task = await ax.enqueue("analyze_research", AnalysisContext(
250
+ brand=brand_result,
251
+ market=market_result,
252
+ ))
253
+ return await ax.get_result(task)
254
+
247
255
 
256
+ # Queue to worker (non-blocking, activity tracked automatically)
257
+ task = await pipeline.enqueue(context=InputContext(company="Anthropic"))
248
258
 
249
- # Run the pipeline
250
- result = await pipeline.run(context=InputContext(company="Anthropic"))
259
+ # Or run inline (blocking)
260
+ result = await pipeline.run(None, InputContext(company="Anthropic"))
251
261
  ```
252
262
 
253
263
  See **[examples/openai-agents-fastapi/pipeline.py](examples/openai-agents-fastapi/pipeline.py)** for a complete example.
254
264
 
265
+ ### Dynamic Fan-Out with Tracker
266
+
267
+ Coordinate tasks that are queued dynamically and trigger a follow-up when all complete:
268
+
269
+ ```python
270
+ import uuid
271
+
272
+ tracker = ax.Tracker("research", uuid.uuid4())
273
+
274
+ @pool.task("research")
275
+ async def research(agent_id: UUID, context: ResearchContext) -> ResearchResult:
276
+ runner = ax.OpenAIRunner(agent_id=agent_id)
277
+ agent = Agent(
278
+ name="Research Agent",
279
+ instructions="...",
280
+ tools=[save_research],
281
+ output_type=ResearchResult,
282
+ )
283
+ return await runner.run(agent, input=f"Research {context.company}")
284
+
285
+
286
+ @pool.task("aggregate")
287
+ async def aggregate(agent_id: UUID, context: AggregateContext) -> None:
288
+ # ... aggregate results ...
289
+
290
+ @function_tool
291
+ async def queue_research(company: str) -> None:
292
+ tracker.incr()
293
+ await ax.enqueue("research", ResearchContext(company=company, batch_id=batch_id))
294
+
295
+ @function_tool
296
+ async def save_research(context: ResearchResult) -> None:
297
+ # save_research_to_database(context)
298
+ tracker.decr()
299
+ if tracker.complete:
300
+ await ax.enqueue("aggregate", ...)
301
+
302
+
303
+ ```
304
+
255
305
  ---
256
306
 
257
307
  ## Full Example: FastAPI Integration
@@ -317,7 +367,7 @@ class MyContext(BaseModel):
317
367
  param: str
318
368
 
319
369
 
320
- pool = ax.WorkerPool(engine=engine)
370
+ pool = ax.Pool(engine=engine)
321
371
 
322
372
 
323
373
  @pool.task("task_name")
@@ -389,11 +439,54 @@ AgentExec creates two tables (prefix configurable):
389
439
  - `activity_id` - Foreign key to activity
390
440
  - `message` - Log message
391
441
  - `status` - QUEUED, RUNNING, COMPLETE, ERROR, CANCELED
392
- - `completion_percentage` - Progress (0-100)
442
+ - `percentage` - Progress (0-100)
393
443
  - `created_at` - When log was created
394
444
 
395
445
  ---
396
446
 
447
+ ## Docker Deployment
448
+
449
+ Deploy workers using the official Docker image:
450
+
451
+ ### 1. Create your worker Dockerfile
452
+
453
+ ```dockerfile
454
+ FROM ghcr.io/agent-ci/agentexec-worker:latest
455
+
456
+ COPY ./src /app/src
457
+ ENV AGENTEXEC_WORKER_MODULE=src.worker
458
+ ```
459
+
460
+ ### 2. Create your worker module
461
+
462
+ ```python
463
+ # src/worker.py
464
+ import os
465
+ import agentexec as ax
466
+
467
+ pool = ax.Pool(database_url=os.environ["DATABASE_URL"])
468
+
469
+ @pool.task("my_task")
470
+ async def my_task(agent_id, context):
471
+ # Your task implementation
472
+ pass
473
+ ```
474
+
475
+ ### 3. Run with Docker
476
+
477
+ ```bash
478
+ docker build -t my-worker .
479
+ docker run \
480
+ -e DATABASE_URL=postgresql://... \
481
+ -e REDIS_URL=redis://... \
482
+ -e OPENAI_API_KEY=sk-... \
483
+ my-worker
484
+ ```
485
+
486
+ See **[docker/README.md](docker/README.md)** for full documentation including Docker Compose examples.
487
+
488
+ ---
489
+
397
490
  ## Development
398
491
 
399
492
  ```bash
@@ -408,7 +501,7 @@ uv sync
408
501
  uv run pytest
409
502
 
410
503
  # Type checking
411
- uv run mypy src/agentexec
504
+ uv run ty check
412
505
 
413
506
  # Linting
414
507
  uv run ruff check src/