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.
- agentexec-0.1.4/.claude/skills/prepare-release/SKILL.md +57 -0
- agentexec-0.1.4/.github/workflows/docker-publish.yml +65 -0
- agentexec-0.1.4/.github/workflows/npm-publish.yml +46 -0
- agentexec-0.1.4/CHANGELOG.md +211 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/PKG-INFO +136 -43
- {agentexec-0.1.2 → agentexec-0.1.4}/README.md +132 -39
- agentexec-0.1.4/docker/Dockerfile +77 -0
- agentexec-0.1.4/docker/README.md +149 -0
- agentexec-0.1.4/docker/entrypoint.py +78 -0
- agentexec-0.1.4/docs/api-reference/activity.md +455 -0
- agentexec-0.1.4/docs/api-reference/core.md +476 -0
- agentexec-0.1.4/docs/api-reference/pipeline.md +529 -0
- agentexec-0.1.4/docs/api-reference/runner.md +430 -0
- agentexec-0.1.4/docs/concepts/activity-tracking.md +520 -0
- agentexec-0.1.4/docs/concepts/architecture.md +344 -0
- agentexec-0.1.4/docs/concepts/task-lifecycle.md +372 -0
- agentexec-0.1.4/docs/concepts/worker-pool.md +499 -0
- agentexec-0.1.4/docs/contributing.md +341 -0
- agentexec-0.1.4/docs/deployment/docker.md +557 -0
- agentexec-0.1.4/docs/deployment/production.md +597 -0
- agentexec-0.1.4/docs/getting-started/configuration.md +295 -0
- agentexec-0.1.4/docs/getting-started/installation.md +34 -0
- agentexec-0.1.4/docs/getting-started/quickstart.md +262 -0
- agentexec-0.1.4/docs/guides/basic-usage.md +437 -0
- agentexec-0.1.4/docs/guides/custom-runners.md +595 -0
- agentexec-0.1.4/docs/guides/fastapi-integration.md +301 -0
- agentexec-0.1.4/docs/guides/openai-runner.md +468 -0
- agentexec-0.1.4/docs/guides/pipelines.md +553 -0
- agentexec-0.1.4/docs/index.md +129 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/README.md +45 -1
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/pipeline.py +10 -3
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/.gitignore +3 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/bun.lock +422 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/index.html +13 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/package.json +27 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/public/vite.svg +4 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/App.tsx +35 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/api/agents.ts +37 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/api/queries.ts +51 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/components/Layout.tsx +38 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/index.css +263 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/main.tsx +10 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/pages/AgentDetailPage.tsx +45 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/pages/AgentListPage.tsx +68 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/src/styles/github-dark.css +617 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/tsconfig.json +21 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/tsconfig.node.json +11 -0
- agentexec-0.1.4/examples/openai-agents-fastapi/ui/vite.config.ts +19 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/views.py +19 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/worker.py +4 -1
- {agentexec-0.1.2 → agentexec-0.1.4}/pyproject.toml +11 -12
- agentexec-0.1.4/src/agentexec/__init__.py +64 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/activity/__init__.py +6 -17
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/activity/models.py +46 -7
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/activity/schemas.py +2 -2
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/activity/tracker.py +23 -12
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/config.py +12 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/core/queue.py +13 -16
- agentexec-0.1.4/src/agentexec/core/results.py +64 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/core/task.py +91 -47
- agentexec-0.1.4/src/agentexec/pipeline.py +454 -0
- agentexec-0.1.4/src/agentexec/runners/__init__.py +5 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/runners/base.py +14 -10
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/runners/openai.py +10 -13
- agentexec-0.1.4/src/agentexec/state/__init__.py +216 -0
- agentexec-0.1.4/src/agentexec/state/backend.py +275 -0
- agentexec-0.1.4/src/agentexec/state/redis_backend.py +380 -0
- agentexec-0.1.4/src/agentexec/tracker.py +67 -0
- agentexec-0.1.4/src/agentexec/worker/__init__.py +5 -0
- agentexec-0.1.4/src/agentexec/worker/event.py +48 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/worker/logging.py +10 -11
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/worker/pool.py +62 -50
- agentexec-0.1.4/tests/test_activity_schemas.py +224 -0
- agentexec-0.1.4/tests/test_activity_tracking.py +355 -0
- agentexec-0.1.4/tests/test_config.py +204 -0
- agentexec-0.1.4/tests/test_db.py +134 -0
- agentexec-0.1.4/tests/test_pipeline.py +172 -0
- agentexec-0.1.4/tests/test_pipeline_flow.py +437 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/tests/test_public_api.py +20 -29
- agentexec-0.1.4/tests/test_queue.py +209 -0
- agentexec-0.1.4/tests/test_results.py +193 -0
- agentexec-0.1.4/tests/test_runners.py +565 -0
- agentexec-0.1.4/tests/test_self_describing_results.py +126 -0
- agentexec-0.1.4/tests/test_state.py +185 -0
- agentexec-0.1.4/tests/test_state_backend.py +292 -0
- agentexec-0.1.4/tests/test_task.py +319 -0
- agentexec-0.1.4/tests/test_worker_event.py +133 -0
- agentexec-0.1.4/tests/test_worker_logging.py +280 -0
- agentexec-0.1.4/tests/test_worker_pool.py +247 -0
- agentexec-0.1.4/ui/.gitignore +3 -0
- agentexec-0.1.4/ui/README.md +148 -0
- agentexec-0.1.4/ui/bun.lock +397 -0
- agentexec-0.1.4/ui/package.json +52 -0
- agentexec-0.1.4/ui/src/components/ActiveAgentsBadge.tsx +20 -0
- agentexec-0.1.4/ui/src/components/ProgressBar.tsx +24 -0
- agentexec-0.1.4/ui/src/components/StatusBadge.tsx +42 -0
- agentexec-0.1.4/ui/src/components/TaskDetail.tsx +121 -0
- agentexec-0.1.4/ui/src/components/TaskList.tsx +119 -0
- agentexec-0.1.4/ui/src/components/index.ts +5 -0
- agentexec-0.1.4/ui/src/index.ts +23 -0
- agentexec-0.1.4/ui/src/types.ts +59 -0
- agentexec-0.1.4/ui/tsconfig.json +24 -0
- agentexec-0.1.4/ui/vite.config.ts +34 -0
- agentexec-0.1.2/CHANGELOG.md +0 -82
- agentexec-0.1.2/src/agentexec/__init__.py +0 -72
- agentexec-0.1.2/src/agentexec/core/redis_client.py +0 -71
- agentexec-0.1.2/src/agentexec/core/results.py +0 -62
- agentexec-0.1.2/src/agentexec/pipeline.py +0 -199
- agentexec-0.1.2/src/agentexec/runners/__init__.py +0 -13
- agentexec-0.1.2/src/agentexec/worker/__init__.py +0 -7
- agentexec-0.1.2/src/agentexec/worker/event.py +0 -43
- agentexec-0.1.2/tests/test_activity_tracking.py +0 -89
- agentexec-0.1.2/tests/test_redis_client.py +0 -63
- agentexec-0.1.2/tests/test_task.py +0 -169
- agentexec-0.1.2/tests/test_worker_pool.py +0 -145
- {agentexec-0.1.2 → agentexec-0.1.4}/.github/workflows/publish.yml +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/.gitignore +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/alembic/README +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/alembic/env.py +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/alembic/script.py.mako +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/alembic.ini +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/compose.yml +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/context.py +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/db.py +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/main.py +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/models.py +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/pyproject.toml +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/examples/openai-agents-fastapi/tools.py +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/core/__init__.py +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/core/db.py +0 -0
- {agentexec-0.1.2 → agentexec-0.1.4}/src/agentexec/core/logging.py +0 -0
- {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.
|
|
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@
|
|
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
|
-
|
|
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
|
+
[](https://pypi.org/project/agentexec/)
|
|
29
|
+
[](https://www.python.org/)
|
|
30
|
+
[](LICENSE)
|
|
31
|
+
[](https://github.com/astral-sh/ty)
|
|
32
|
+
[](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.
|
|
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
|
-
#
|
|
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.
|
|
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"
|
|
109
|
-
|
|
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-
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
|
239
|
+
@pipeline.step(0, "brand and market research")
|
|
240
|
+
async def parallel_research(self, context: InputContext):
|
|
237
241
|
"""Run multiple tasks in parallel."""
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
return await ax.gather(
|
|
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("
|
|
246
|
-
|
|
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
|
-
#
|
|
250
|
-
result = await pipeline.run(
|
|
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.
|
|
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
|
-
- `
|
|
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
|
|
504
|
+
uv run ty check
|
|
412
505
|
|
|
413
506
|
# Linting
|
|
414
507
|
uv run ruff check src/
|