ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
"""Offline contract tests; these are not substitutes for the live campaign receipt."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
MODULE_PATH = HERE / "unipile_mailbox_experiment.py"
|
||||
SPEC = importlib.util.spec_from_file_location("experiment_6_1_unipile", MODULE_PATH)
|
||||
experiment = importlib.util.module_from_spec(SPEC)
|
||||
assert SPEC.loader is not None
|
||||
sys.modules[SPEC.name] = experiment
|
||||
SPEC.loader.exec_module(experiment)
|
||||
|
||||
|
||||
def _email(kind: str, date: str, suffix: str) -> dict:
|
||||
base = {"id": f"email-{suffix}", "account_id": "account-real",
|
||||
"date": date, "role": "inbox", "folders": ["Inbox"]}
|
||||
if kind == "meeting_invitation":
|
||||
return {**base, "subject": "Meeting invitation: design review",
|
||||
"body_plain": "START_UTC: 2026-08-03T10:00:00.000Z\n"
|
||||
"END_UTC: 2026-08-03T11:00:00.000Z"}
|
||||
if kind == "customer_complaint":
|
||||
return {**base, "subject": "Customer complaint: delayed order",
|
||||
"body_plain": "Customer complaint for order #E44-TEST. Please escalate."}
|
||||
return {**base, "subject": "Marketing newsletter",
|
||||
"body_plain": "Marketing newsletter promotion. Click to unsubscribe."}
|
||||
|
||||
|
||||
def _provider(request: httpx.Request) -> httpx.Response:
|
||||
assert request.headers.get("X-API-KEY") == "unit-secret"
|
||||
if request.method == "GET" and request.url.path == "/api/v1/calendars":
|
||||
return httpx.Response(200, json={"data": [{"id": "calendar-real",
|
||||
"is_primary": True}]})
|
||||
if request.method == "GET" and request.url.path.endswith("/events"):
|
||||
return httpx.Response(200, json={"data": []})
|
||||
if request.method == "GET" and request.url.path == "/api/v1/folders":
|
||||
return httpx.Response(200, json={"items": [{"id": "folder-archive",
|
||||
"name": "Archive",
|
||||
"role": "archive"}]})
|
||||
if request.method == "PUT" and request.url.path == "/api/v1/emails/email-marketing":
|
||||
assert json.loads(request.content) == {"folders": ["archive"]}
|
||||
return httpx.Response(200, json={"object": "EmailUpdated"})
|
||||
if request.method == "GET" and request.url.path == "/api/v1/emails/email-marketing":
|
||||
return httpx.Response(200, json={"id": "email-marketing",
|
||||
"role": "archive", "folders": ["Archive"]})
|
||||
return httpx.Response(404, json={"type": "unexpected_test_request"})
|
||||
|
||||
|
||||
def test_classification_is_unique_and_meeting_interval_is_exact():
|
||||
email = _email("meeting_invitation", "2026-08-01T00:00:01.000Z", "meeting")
|
||||
assert experiment.classify_email(email) == "meeting_invitation"
|
||||
start, end = experiment.meeting_interval(email)
|
||||
assert start.isoformat() == "2026-08-03T10:00:00+00:00"
|
||||
assert (end - start).total_seconds() == 3600
|
||||
ambiguous = {**email, "subject": "Meeting invitation and marketing newsletter",
|
||||
"body_plain": email["body_plain"] + "\nMarketing unsubscribe"}
|
||||
with pytest.raises(ValueError, match="not unique"):
|
||||
experiment.classify_email(ambiguous)
|
||||
|
||||
|
||||
def test_real_api_error_is_receipted_and_raises():
|
||||
def unauthorized(_: httpx.Request) -> httpx.Response:
|
||||
return httpx.Response(401, json={"status": 401,
|
||||
"type": "errors/missing_credentials",
|
||||
"title": "Missing credentials"})
|
||||
|
||||
client = experiment.UnipileClient(
|
||||
"api.example.invalid:12345", "unit-secret",
|
||||
transport=httpx.MockTransport(unauthorized),
|
||||
)
|
||||
with pytest.raises(experiment.UnipileAPIError):
|
||||
client.list_accounts()
|
||||
assert client.calls == [{
|
||||
**client.calls[0], "status": 401, "success": False,
|
||||
"credential_scheme": "X-API-KEY",
|
||||
"error_type": "errors/missing_credentials",
|
||||
}]
|
||||
assert "unit-secret" not in experiment.canonical_json(client.calls)
|
||||
client.close()
|
||||
|
||||
def test_three_email_workflow_and_acceptance(tmp_path):
|
||||
client = experiment.UnipileClient(
|
||||
"api.example.invalid:12345", "unit-secret",
|
||||
transport=httpx.MockTransport(_provider),
|
||||
)
|
||||
runner = experiment.MailboxExperiment(
|
||||
client, tmp_path, "account-real", "account-real"
|
||||
)
|
||||
# Deliberately unordered input proves that the FIFO queue uses provider time.
|
||||
emails = [
|
||||
_email("marketing", "2026-08-01T00:00:03.000Z", "marketing"),
|
||||
_email("meeting_invitation", "2026-08-01T00:00:01.000Z", "meeting"),
|
||||
_email("customer_complaint", "2026-08-01T00:00:02.000Z", "complaint"),
|
||||
]
|
||||
runner.process_queue(emails)
|
||||
assert [row["classification"] for row in runner.workflows] == [
|
||||
"meeting_invitation", "customer_complaint", "marketing"
|
||||
]
|
||||
result = experiment.derive_acceptance(
|
||||
runner.events, runner.workflows, client.calls,
|
||||
[{"object": "EmailSent"}] * 3,
|
||||
credential_secret="unit-secret", dsn_secret="api.example.invalid:12345",
|
||||
)
|
||||
assert result["status"] == "passed"
|
||||
assert all(result["gates"].values())
|
||||
assert (tmp_path / "artifacts" / "meeting_reply_draft.txt").stat().st_size > 100
|
||||
assert (tmp_path / "artifacts" / "high_priority_notifications.jsonl").is_file()
|
||||
assert "unit-secret" not in experiment.canonical_json(client.calls)
|
||||
client.close()
|
||||
|
||||
|
||||
def test_acceptance_fails_when_provider_archive_verification_is_missing(tmp_path):
|
||||
client = experiment.UnipileClient(
|
||||
"api.example.invalid:12345", "unit-secret",
|
||||
transport=httpx.MockTransport(_provider),
|
||||
)
|
||||
runner = experiment.MailboxExperiment(client, tmp_path, "account-real", "account-real")
|
||||
runner.process_queue([
|
||||
_email("meeting_invitation", "2026-08-01T00:00:01.000Z", "meeting"),
|
||||
_email("customer_complaint", "2026-08-01T00:00:02.000Z", "complaint"),
|
||||
_email("marketing", "2026-08-01T00:00:03.000Z", "marketing"),
|
||||
])
|
||||
runner.workflows[-1]["archive"]["verified"] = False
|
||||
result = experiment.derive_acceptance(
|
||||
runner.events, runner.workflows, client.calls, [{}, {}, {}],
|
||||
credential_secret="unit-secret", dsn_secret="api.example.invalid:12345",
|
||||
)
|
||||
assert result["status"] == "failed"
|
||||
assert not result["gates"]["marketing_archived_and_verified_through_provider"]
|
||||
client.close()
|
||||
Reference in New Issue
Block a user