ad-tech-inc-resilient-http 0.1.0__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 (47) hide show
  1. ad_tech_inc_resilient_http-0.1.0/.gemini/agents/issue-runner.sh +96 -0
  2. ad_tech_inc_resilient_http-0.1.0/.gemini/prompts/issue-worker.md +30 -0
  3. ad_tech_inc_resilient_http-0.1.0/.gemini/skills/github-issues/SKILL.md +118 -0
  4. ad_tech_inc_resilient_http-0.1.0/.gemini/skills/resilience-expert/SKILL.md +39 -0
  5. ad_tech_inc_resilient_http-0.1.0/.gemini/skills/resilience-expert/references/architecture.md +23 -0
  6. ad_tech_inc_resilient_http-0.1.0/.gemini/skills/resilience-expert/references/testing-patterns.md +44 -0
  7. ad_tech_inc_resilient_http-0.1.0/.github/workflows/ci.yml +35 -0
  8. ad_tech_inc_resilient_http-0.1.0/.github/workflows/publish.yml +33 -0
  9. ad_tech_inc_resilient_http-0.1.0/.gitignore +10 -0
  10. ad_tech_inc_resilient_http-0.1.0/.python-version +1 -0
  11. ad_tech_inc_resilient_http-0.1.0/.vscode/settings.json +5 -0
  12. ad_tech_inc_resilient_http-0.1.0/GEMINI.md +32 -0
  13. ad_tech_inc_resilient_http-0.1.0/PKG-INFO +334 -0
  14. ad_tech_inc_resilient_http-0.1.0/README.md +317 -0
  15. ad_tech_inc_resilient_http-0.1.0/docker-compose.yml +11 -0
  16. ad_tech_inc_resilient_http-0.1.0/docs/close.mmd +10 -0
  17. ad_tech_inc_resilient_http-0.1.0/docs/close.png +0 -0
  18. ad_tech_inc_resilient_http-0.1.0/docs/closev2.png +0 -0
  19. ad_tech_inc_resilient_http-0.1.0/docs/diagrams/architecture.mmd +17 -0
  20. ad_tech_inc_resilient_http-0.1.0/docs/diagrams/sequence_flow.mmd +33 -0
  21. ad_tech_inc_resilient_http-0.1.0/docs/diagrams/state_machine.mmd +11 -0
  22. ad_tech_inc_resilient_http-0.1.0/docs/half_open.mmd +13 -0
  23. ad_tech_inc_resilient_http-0.1.0/docs/half_open.png +0 -0
  24. ad_tech_inc_resilient_http-0.1.0/docs/open.mmd +7 -0
  25. ad_tech_inc_resilient_http-0.1.0/docs/open.png +0 -0
  26. ad_tech_inc_resilient_http-0.1.0/examples/fastapi_integration.py +48 -0
  27. ad_tech_inc_resilient_http-0.1.0/examples/simulate_outage.py +78 -0
  28. ad_tech_inc_resilient_http-0.1.0/examples/slack_example.py +46 -0
  29. ad_tech_inc_resilient_http-0.1.0/examples/stripe_example.py +44 -0
  30. ad_tech_inc_resilient_http-0.1.0/pyproject.toml +30 -0
  31. ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/__init__.py +20 -0
  32. ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/circuit_breaker.py +94 -0
  33. ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/client.py +84 -0
  34. ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/config.py +11 -0
  35. ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/exceptions.py +10 -0
  36. ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/failure_store.py +125 -0
  37. ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/fallback.py +21 -0
  38. ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/http.py +48 -0
  39. ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/retry.py +22 -0
  40. ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/types.py +7 -0
  41. ad_tech_inc_resilient_http-0.1.0/tests/test_circuit_breaker.py +196 -0
  42. ad_tech_inc_resilient_http-0.1.0/tests/test_failure_store.py +94 -0
  43. ad_tech_inc_resilient_http-0.1.0/tests/test_fallback.py +44 -0
  44. ad_tech_inc_resilient_http-0.1.0/tests/test_flaky_resilience.py +127 -0
  45. ad_tech_inc_resilient_http-0.1.0/tests/test_integration.py +97 -0
  46. ad_tech_inc_resilient_http-0.1.0/tests/test_retry_policy.py +20 -0
  47. ad_tech_inc_resilient_http-0.1.0/uv.lock +333 -0
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ #-------------------------
6
+ # Config
7
+ #-------------------------
8
+ ITERATIONS="${1:-10}"
9
+
10
+ MAIN_BRANCH="${MAIN_BRANCH:-main}"
11
+ BRANCH="${BRANCH:-overnight-batch-$(date +%Y%m%d-%H%M%S)}"
12
+
13
+ PROMPT_FILE=".gemini/prompts/issue-worker.md"
14
+
15
+ SUCCESS_FILE=".agent-complete"
16
+ STUCK_FILE=".agent-stuck"
17
+
18
+ #-------------------------
19
+ # Helpers
20
+ #-------------------------
21
+ cleanup() {
22
+ rm -f "$SUCCESS_FILE" "$STUCK_FILE"
23
+ }
24
+
25
+ trap cleanup EXIT
26
+
27
+ #-------------------------
28
+ # Validate
29
+ #-------------------------
30
+ if ! command -v gemini >/dev/null 2>&1; then
31
+ echo "gemini command not found."
32
+ exit 1
33
+ fi
34
+
35
+ if ! command -v gh >/dev/null 2>&1; then
36
+ echo "GitHub CLI (gh) is not installed. Please install it to use this agent."
37
+ exit 1
38
+ fi
39
+
40
+ if [ ! -f "$PROMPT_FILE" ]; then
41
+ echo "Prompt file missing: $PROMPT_FILE"
42
+ exit 1
43
+ fi
44
+
45
+ if [ -n "$(git status --porcelain)" ]; then
46
+ echo "Working directory is dirty. Commit or stash changes first."
47
+ exit 1
48
+ fi
49
+
50
+ #-------------------------
51
+ # Setup
52
+ #-------------------------
53
+ git checkout "$MAIN_BRANCH"
54
+ git pull origin "$MAIN_BRANCH"
55
+
56
+ git checkout -b "$BRANCH"
57
+
58
+ echo "Branch: $BRANCH"
59
+ echo "Iterations: $ITERATIONS"
60
+ echo "Prompt: $PROMPT_FILE"
61
+
62
+ #-------------------------
63
+ # Agent Loop
64
+ #-------------------------
65
+ for ((i=1; i<=ITERATIONS; i++)); do
66
+
67
+ echo
68
+ echo "=== Iteration $i/$ITERATIONS ==="
69
+
70
+ if ! timeout 5m gemini -p "$(cat "$PROMPT_FILE")"; then
71
+ echo "Gemini timed out"
72
+ exit 1
73
+ fi
74
+
75
+ if [ -f "$STUCK_FILE" ]; then
76
+ echo "Agent stuck:"
77
+ cat "$STUCK_FILE"
78
+ exit 1
79
+ fi
80
+
81
+ if [ -f "$SUCCESS_FILE" ]; then
82
+ echo "All tasks completed."
83
+ break
84
+ fi
85
+
86
+ done
87
+
88
+ #-------------------------
89
+ # Push
90
+ #-------------------------
91
+ git push -u origin "$BRANCH"
92
+
93
+ echo
94
+ echo "Finished."
95
+ echo "Review:"
96
+ echo "git log $MAIN_BRANCH..$BRANCH"
@@ -0,0 +1,30 @@
1
+ # Issue Worker
2
+
3
+ ## Goal
4
+
5
+ Complete one issue at a time.
6
+
7
+ ## Instructions
8
+
9
+ 1. Use github-issues skill to fetch open issues.
10
+ 2. Check existing Tasks and skip issues already completed or in_progress.
11
+ 3. Pick the most appropriate remaining issue.
12
+ 4. Create a Task and mark it in_progress.
13
+ 5. Implement the fix or feature.
14
+ 6. Add tests if needed.
15
+ 7. Run formatting.
16
+ 8. Run the full test suite.
17
+
18
+ If tests fail:
19
+ - attempt fixes
20
+ - after 3 failed attempts mark the Task stuck
21
+ - create .agent-stuck with issue number and reason
22
+
23
+ If no issues remain:
24
+ - create .agent-complete
25
+ - exit
26
+
27
+ Commit changes:
28
+ ISSUE #<number>: <short description>
29
+
30
+ Mark the Task completed.
@@ -0,0 +1,118 @@
1
+ ---
2
+ name: github-issues
3
+ description: Use this skill whenever work should be selected from GitHub issues, tasks need tracking, or an autonomous agent is processing repository issues.
4
+ ---
5
+
6
+ # GitHub Issues Skill
7
+
8
+ ## Purpose
9
+
10
+ Process repository issues one at a time in a predictable and repeatable manner.
11
+
12
+ ## Workflow
13
+
14
+ ### 1. Fetch Issues
15
+
16
+ Retrieve all open GitHub issues.
17
+
18
+ Ignore:
19
+
20
+ * closed issues
21
+ * duplicate issues
22
+ * blocked issues
23
+ * issues already marked completed
24
+ * issues already marked in_progress
25
+
26
+ ### 2. Select Issue
27
+
28
+ Choose exactly one issue.
29
+
30
+ Priority order:
31
+
32
+ 1. high priority
33
+ 2. oldest open issue
34
+ 3. smallest independent issue
35
+
36
+ ### 3. Start Task
37
+
38
+ Create or update task tracking.
39
+
40
+ Mark issue status:
41
+
42
+ in_progress
43
+
44
+ Record:
45
+
46
+ * issue number
47
+ * title
48
+
49
+ ### 4. Implement
50
+
51
+ Implement only the changes necessary to satisfy the issue.
52
+
53
+ Avoid unrelated refactoring.
54
+
55
+ Follow existing project conventions.
56
+
57
+ ### 5. Testing
58
+
59
+ Add or update tests if required.
60
+
61
+ Run formatting.
62
+
63
+ Run the complete test suite.
64
+
65
+ ### 6. Failure Handling
66
+
67
+ If tests fail:
68
+
69
+ 1. investigate
70
+ 2. fix
71
+ 3. rerun tests
72
+
73
+ Maximum attempts: 3
74
+
75
+ If still failing:
76
+
77
+ Create:
78
+
79
+ .agent-stuck
80
+
81
+ Contents:
82
+
83
+ Issue #<number>
84
+ Reason: <explanation>
85
+
86
+ Mark task:
87
+
88
+ stuck
89
+
90
+ Stop execution.
91
+
92
+ ### 7. Success Handling
93
+
94
+ Commit changes:
95
+
96
+ ISSUE #<number>: <short description>
97
+
98
+ Mark task:
99
+
100
+ completed
101
+
102
+ ### 8. Completion
103
+
104
+ If no issues remain:
105
+
106
+ Create:
107
+
108
+ .agent-complete
109
+
110
+ Exit successfully.
111
+
112
+ ## Rules
113
+
114
+ * Work on exactly one issue.
115
+ * Never skip testing.
116
+ * Never skip formatting.
117
+ * Never create both `.agent-complete` and `.agent-stuck`.
118
+ * Prefer small reviewable commits.
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: resilience-expert
3
+ description: Expert guidance for maintaining, extending, and debugging the Resilient HTTP Client library. Use when adding resilience patterns, new storage backends, or fixing integration bugs in examples and tests.
4
+ ---
5
+
6
+ # Resilience Expert
7
+
8
+ Expert guidance for the `resilient-http-client` library.
9
+
10
+ ## Core Concepts
11
+
12
+ - **Architecture**: See [architecture.md](references/architecture.md) for how components coordinate.
13
+ - **Testing**: See [testing-patterns.md](references/testing-patterns.md) for standardized mocks.
14
+
15
+ ## Workflows
16
+
17
+ ### Extending the Library
18
+
19
+ 1. **New Resilience Pattern**: Implement the logic in a new module, integrate it into `ResilientHttpClient.request()`, and add configuration to `ResilienceConfig`.
20
+ 2. **New Storage Backend**: Inherit from the same interface used by `FailureStore` (methods like `get_state`, `set_state`, `increment_failures`).
21
+
22
+ ### Debugging Issues
23
+
24
+ 1. **Verify State**: Check the `FailureStore` state in Redis using `redis-cli`.
25
+ 2. **Check Transitions**: Use `simulate_outage.py` to verify Circuit Breaker transitions (CLOSED -> OPEN -> HALF-OPEN -> CLOSED).
26
+ 3. **Await Everything**: Ensure all async methods in `FailureStore` and `Client` are properly awaited.
27
+
28
+ ### Testing Requirements
29
+
30
+ - Always add unit tests in `tests/`.
31
+ - Use `pytest-asyncio` for async tests.
32
+ - Maintain consistency with `FakeRedis` and `MockResponse` mocks.
33
+ - Ensure `PYTHONPATH=.` is set when running tests.
34
+
35
+ ## Rules
36
+
37
+ - Use `CircuitState` enum for all state-related logic.
38
+ - Prefer `async with` context managers for `ResilientHttpClient`.
39
+ - Maintain docstrings for public interfaces in `src/client.py`.
@@ -0,0 +1,23 @@
1
+ # Resilient HTTP Client Architecture
2
+
3
+ The library is designed around the **Orchestrator** pattern where `ResilientHttpClient` coordinates several specialized components.
4
+
5
+ ## Component Coordination
6
+
7
+ 1. **ResilientHttpClient**: The entry point. It manages the lifecycle of the request.
8
+ 2. **CircuitBreaker**: Consulted before every request. Updates state based on success/failure.
9
+ 3. **FailureStore**: Persistent storage (e.g., Redis) for failure metrics and circuit state.
10
+ 4. **RetryPolicy**: Determines if and when to retry based on exponential backoff.
11
+ 5. **HttpExecutor**: Wraps `httpx` and normalizes responses into `HttpResponse`.
12
+ 6. **FallbackHandler**: Executes registered fallback logic when all else fails.
13
+
14
+ ## Request Flow
15
+
16
+ 1. Check `circuit.allow_request()`.
17
+ 2. If blocked, return `fallback.run("circuit_open")`.
18
+ 3. Loop for retries:
19
+ a. Execute `http.send()`.
20
+ b. If success, `circuit.on_success()` and return data.
21
+ c. If failure, `circuit.on_failure()`.
22
+ d. Check `retry.can_retry()`. If yes, `retry.wait()` and continue.
23
+ 4. If loop exits, return `fallback.run(last_error)`.
@@ -0,0 +1,44 @@
1
+ # Testing Patterns
2
+
3
+ Consistency in testing is critical to ensure mocks correctly simulate the production components.
4
+
5
+ ## FakeRedis Mock
6
+
7
+ Always use the standardized `FakeRedis` for unit and integration tests. It must support `nx` and `ex` arguments.
8
+
9
+ ```python
10
+ class FakeRedis:
11
+ def __init__(self):
12
+ self.db = {}
13
+
14
+ async def get(self, key):
15
+ return self.db.get(key)
16
+
17
+ async def set(self, key, value, ex=None, nx=False):
18
+ if nx and key in self.db:
19
+ return False
20
+ self.db[key] = value
21
+ return True
22
+
23
+ async def incr(self, key):
24
+ self.db[key] = int(self.db.get(key, 0)) + 1
25
+ return self.db[key]
26
+
27
+ async def expire(self, key, ttl, nx=False):
28
+ pass # Optional: implement for TTL specific tests
29
+
30
+ async def delete(self, key):
31
+ self.db.pop(key, None)
32
+ ```
33
+
34
+ ## Mocking HTTP Responses
35
+
36
+ When mocking `HttpExecutor`, return a `MockResponse` object that implements `is_success`, `status_code`, and `data`.
37
+
38
+ ```python
39
+ class MockResponse:
40
+ def __init__(self, is_success, status_code, data):
41
+ self.is_success = is_success
42
+ self.status_code = status_code
43
+ self.data = data
44
+ ```
@@ -0,0 +1,35 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+ name: Test on Python ${{ matrix.python-version }}
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ matrix:
15
+ python-version: ["3.12"]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v5
22
+ with:
23
+ enable-cache: true
24
+ version: "latest"
25
+
26
+ - name: Set up Python
27
+ uses: actions/setup-python@v5
28
+ with:
29
+ python-version: ${{ matrix.python-version }}
30
+
31
+ - name: Install dependencies
32
+ run: uv sync --all-extras --dev
33
+
34
+ - name: Run pytest
35
+ run: uv run pytest
@@ -0,0 +1,33 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ name: Build and Publish
10
+ runs-on: ubuntu-latest
11
+ environment: pypi
12
+ permissions:
13
+ id-token: write # Required for trusted publishing
14
+ contents: read
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@v5
20
+ with:
21
+ enable-cache: true
22
+ version: "latest"
23
+
24
+ - name: Set up Python
25
+ uses: actions/setup-python@v5
26
+ with:
27
+ python-version: "3.12"
28
+
29
+ - name: Build package
30
+ run: uv build
31
+
32
+ - name: Publish package to PyPI
33
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,10 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,5 @@
1
+ {
2
+ "cSpell.words": [
3
+ "cooldown"
4
+ ]
5
+ }
@@ -0,0 +1,32 @@
1
+ # ๐Ÿ›ก๏ธ Resilient HTTP Client - Project Instructions
2
+
3
+ This file provides foundational guidance for AI agents working on the `resilient-http-client` project.
4
+
5
+ ## ๐Ÿ—๏ธ Architectural Core
6
+ This library is built on the principle of **strict state management** for resilience patterns.
7
+ - **State Machine**: All circuit breaker logic must strictly follow the `CLOSED`, `OPEN`, `HALF-OPEN` transitions.
8
+ - **Async First**: All network and storage operations MUST be asynchronous and properly awaited.
9
+ - **Pluggable Storage**: The `FailureStore` is the source of truth. Ensure any changes maintain compatibility with distributed backends (like Redis).
10
+
11
+ ## ๐Ÿ› ๏ธ Tech Stack & Tooling
12
+ - **Language**: Python 3.12+
13
+ - **HTTP Engine**: `httpx` (Asynchronous)
14
+ - **Dependency Management**: `uv`
15
+ - **Testing**: `pytest` with `pytest-asyncio`
16
+ - **Quality**: Adhere to PEP 8; use explicit type hints for all public APIs.
17
+
18
+ ## ๐Ÿงช Testing Mandates
19
+ A change is not complete without verification.
20
+ - **Unit Tests**: Every new resilience logic must have 100% branch coverage in `tests/`.
21
+ - **Chaos Testing**: When modifying `CircuitBreaker` or `RetryPolicy`, you must run or update `tests/test_flaky_resilience.py` to simulate real-world failure modes.
22
+ - **Integration**: Verify changes against the Redis backend if storage logic is modified.
23
+
24
+ ## ๐Ÿ”„ Workflows
25
+ - **Specialized Guidance**: Always activate the `resilience-expert` skill (`activate_skill(name="resilience-expert")`) before making architectural changes.
26
+ - **Environment**: Always run commands with `PYTHONPATH=.` or via `uv run` to ensure local modules are discoverable.
27
+ - **Validation**: Before finishing a task, run `pytest` to ensure no regressions.
28
+
29
+ ## ๐Ÿ“š References
30
+ - Architecture: `docs/diagrams/architecture.mmd`
31
+ - State Machine: `docs/diagrams/state_machine.mmd`
32
+ - Core Implementation: `src/client.py`