Skip to content

Commit e3b9fdc

Browse files
authored
Merge pull request #16 from devonartis/fix/docs-review
Refs devonartis/agentwrit#31
2 parents 07c08ec + 46844ed commit e3b9fdc

4 files changed

Lines changed: 63 additions & 8 deletions

File tree

docs/developer-guide.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,63 @@ else:
430430

431431
---
432432

433+
## Thread Safety
434+
435+
`AgentWritApp` is **not thread-safe for mutations**. The app performs lazy one-time authentication on first use, and `app.close()` mutates internal state. Do not share the same `AgentWritApp` instance across threads or `asyncio` event loops without synchronization.
436+
437+
The underlying `httpx.Client` **is thread-safe for concurrent requests** once the app is authenticated. This means multiple threads can safely call `app.create_agent()` or `agent.validate()` on the *same* authenticated instance, provided no thread calls `close()` while others are using it.
438+
439+
### Recommended Patterns
440+
441+
- **One app per thread** (safest):
442+
```python
443+
import threading
444+
from agentwrit import AgentWritApp
445+
446+
def worker():
447+
app = AgentWritApp(...)
448+
agent = app.create_agent(...)
449+
# ... use agent ...
450+
app.close()
451+
452+
threads = [threading.Thread(target=worker) for _ in range(4)]
453+
for t in threads:
454+
t.start()
455+
```
456+
457+
- **Shared app with explicit lifecycle**:
458+
Create one `AgentWritApp`, use it from many threads, and call `close()` only after all threads have finished.
459+
460+
- **Always call `app.close()`** when you're done to avoid connection leaks.
461+
462+
---
463+
464+
## Async / Await Support
465+
466+
The AgentWrit SDK is **synchronous only** in v0.3.0 (it uses `httpx`'s sync client). There is no native `async`/`await` support planned for this release.
467+
468+
If you are building on an async framework such as FastAPI, Starlette, or Sanic, wrap SDK calls with `asyncio.to_thread()` so they do not block the event loop:
469+
470+
```python
471+
import asyncio
472+
from agentwrit import AgentWritApp
473+
474+
app = AgentWritApp(...)
475+
476+
async def handle_request():
477+
agent = await asyncio.to_thread(
478+
app.create_agent,
479+
orch_id="api-gateway",
480+
task_id="request-123",
481+
requested_scope=["read:data:customer-123"],
482+
ttl=300,
483+
)
484+
# ... use agent ...
485+
await asyncio.to_thread(agent.release)
486+
```
487+
488+
---
489+
433490
## Next Steps
434491

435492
| Guide | What You'll Learn |

docs/sample-apps/07-incident-response.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def check_token(broker_url: str, token: str, label: str) -> bool:
102102

103103
def main() -> None:
104104
broker_url = os.environ["AGENTWRIT_BROKER_URL"]
105-
admin_secret = os.environ.get("AA_ADMIN_SECRET", "dev-secret")
105+
admin_secret = os.environ.get("AGENTWRIT_ADMIN_SECRET", "dev-secret")
106106

107107
app = AgentWritApp(
108108
broker_url=broker_url,
@@ -292,7 +292,7 @@ This app uses the **universal sample app** registered in the [README setup](READ
292292
This app revokes tokens using the admin API, which requires the **operator's admin secret**. This is the same secret used to start the broker:
293293

294294
```bash
295-
export AA_ADMIN_SECRET="dev-secret" # match your broker's admin secret
295+
export AGENTWRIT_ADMIN_SECRET="dev-secret" # match your broker's admin secret
296296
```
297297

298298
### Additional Dependency
@@ -307,7 +307,7 @@ uv add httpx
307307
export AGENTWRIT_BROKER_URL="http://127.0.0.1:8080"
308308
export AGENTWRIT_CLIENT_ID="<from registration>"
309309
export AGENTWRIT_CLIENT_SECRET="<from registration>"
310-
export AA_ADMIN_SECRET="dev-secret"
310+
export AGENTWRIT_ADMIN_SECRET="dev-secret"
311311

312312
uv run python incident_response.py
313313
```

docs/sample-apps/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@ docker compose up -d
7272
### Step 2: Register the Universal Sample App
7373

7474
```bash
75-
export AA_ADMIN_SECRET="dev-secret" # change if your broker uses a different secret
75+
export AGENTWRIT_ADMIN_SECRET="dev-secret" # change if your broker uses a different secret
7676

7777
ADMIN_TOKEN=$(curl -s -X POST http://127.0.0.1:8080/v1/admin/auth \
7878
-H "Content-Type: application/json" \
79-
-d "{\"secret\": \"$AA_ADMIN_SECRET\"}" \
79+
-d "{\"secret\": \"$AGENTWRIT_ADMIN_SECRET\"}" \
8080
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
8181

8282
curl -s -X POST http://127.0.0.1:8080/v1/admin/apps \

docs/testing-guide.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,11 @@ Acceptance tests run against a live broker. They exercise every SDK operation en
4343
With env vars set from the Prerequisites step, run the suite directly with pytest:
4444

4545
```bash
46-
uv run pytest tests/integration/test_acceptance_1_8.py -v -s -m integration
46+
uv run pytest tests/integration/test_acceptance_stories.py -v -s -m integration
4747
```
4848

4949
The `-s` flag is important — it shows the banners in the console.
5050

51-
> **Note on the file name:** `test_acceptance_1_8.py` contains all 15 stories. The name is historical — the suite grew past eight without a rename. A follow-up PR will rename it; until then, `grep "STORY 9"` finds it in the same file.
52-
5351
You can also use the wrapper script, which starts the broker if needed:
5452

5553
```bash

0 commit comments

Comments
 (0)