|
| 1 | +"""Smoke test for the Lago Agent SDK against AWS Bedrock. |
| 2 | +
|
| 3 | +Reads credentials from .env (or the surrounding shell), wraps a Bedrock |
| 4 | +client, makes one converse() call, and flushes the usage event to Lago. |
| 5 | +
|
| 6 | +This is NOT a pytest test — it costs real money (one Bedrock call + one |
| 7 | +Lago event). The filename intentionally avoids pytest's `test_*.py` / |
| 8 | +`*_test.py` discovery patterns so it's never auto-collected. |
| 9 | +
|
| 10 | +Run: |
| 11 | + uv run python tests/smoke.py |
| 12 | +
|
| 13 | +Required env vars: |
| 14 | + LAGO_API_KEY |
| 15 | + LAGO_EXTERNAL_SUBSCRIPTION_ID |
| 16 | + AWS_BEARER_TOKEN_BEDROCK |
| 17 | +""" |
| 18 | + |
| 19 | +from __future__ import annotations |
| 20 | + |
| 21 | +import os |
| 22 | +import pathlib |
| 23 | +import sys |
| 24 | + |
| 25 | +import boto3 |
| 26 | + |
| 27 | +from lago_agent_sdk import LagoSDK |
| 28 | + |
| 29 | +_REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent |
| 30 | + |
| 31 | + |
| 32 | +def _load_dotenv(path: pathlib.Path | None = None) -> None: |
| 33 | + """Tiny no-deps .env loader. Skips comments, blanks, and already-set vars.""" |
| 34 | + p = path or (_REPO_ROOT / ".env") |
| 35 | + if not p.exists(): |
| 36 | + return |
| 37 | + for raw in p.read_text().splitlines(): |
| 38 | + line = raw.strip() |
| 39 | + if not line or line.startswith("#") or "=" not in line: |
| 40 | + continue |
| 41 | + key, _, value = line.partition("=") |
| 42 | + key = key.strip().lstrip("export ").strip() |
| 43 | + value = value.strip().strip("'").strip('"') |
| 44 | + os.environ.setdefault(key, value) |
| 45 | + |
| 46 | + |
| 47 | +def main() -> int: |
| 48 | + _load_dotenv() |
| 49 | + |
| 50 | + try: |
| 51 | + api_key = os.environ["LAGO_API_KEY"] |
| 52 | + subscription = os.environ["LAGO_EXTERNAL_SUBSCRIPTION_ID"] |
| 53 | + except KeyError as missing: |
| 54 | + print(f"error: {missing} is required (set in .env or shell)", file=sys.stderr) |
| 55 | + return 1 |
| 56 | + |
| 57 | + api_url = os.environ.get("LAGO_API_URL", "https://api.getlago.com/api/v1/") |
| 58 | + region = os.environ.get("BEDROCK_REGION", "eu-west-1") |
| 59 | + model_id = os.environ.get("BEDROCK_MODEL_ID", "eu.amazon.nova-lite-v1:0") |
| 60 | + |
| 61 | + sdk = LagoSDK(api_key=api_key, api_url=api_url) |
| 62 | + bedrock = sdk.wrap(boto3.client("bedrock-runtime", region_name=region)) |
| 63 | + |
| 64 | + resp = bedrock.converse( |
| 65 | + modelId=model_id, |
| 66 | + messages=[{"role": "user", "content": [{"text": "what is the capital of France?"}]}], |
| 67 | + extra_lago={"subscription": subscription}, |
| 68 | + ) |
| 69 | + |
| 70 | + print(resp["output"]["message"]["content"][0]["text"]) |
| 71 | + print("\ntokens:", resp["usage"]) |
| 72 | + |
| 73 | + # Ship the usage event before exit — atexit would handle it too, but |
| 74 | + # explicit makes failures (network errors, etc.) visible synchronously. |
| 75 | + if not sdk.flush(timeout=10.0): |
| 76 | + print("warning: lago queue did not drain within 10s", file=sys.stderr) |
| 77 | + return 2 |
| 78 | + return 0 |
| 79 | + |
| 80 | + |
| 81 | +if __name__ == "__main__": |
| 82 | + sys.exit(main()) |
0 commit comments