Build an :class:`ApiKeyPool` from environment variables. Reads ``SKILLSPECTOR_API_KEYS`` — a newline- or semicolon-delimited list of ``key|base_url|model`` entries. Also supports a fallback format where multiple keys are specified via sequentially numbered env vars ``OPENAI_API_KEY
(
max_concurrent_per_key: int = _DEFAULT_MAX_CONCURRENT_PER_KEY,
)
| 550 | |
| 551 | |
| 552 | def create_api_key_pool_from_env( |
| 553 | max_concurrent_per_key: int = _DEFAULT_MAX_CONCURRENT_PER_KEY, |
| 554 | ) -> ApiKeyPool | None: |
| 555 | """Build an :class:`ApiKeyPool` from environment variables. |
| 556 | |
| 557 | Reads ``SKILLSPECTOR_API_KEYS`` — a newline- or semicolon-delimited list |
| 558 | of ``key|base_url|model`` entries. |
| 559 | |
| 560 | Also supports a fallback format where multiple keys are specified via |
| 561 | sequentially numbered env vars ``OPENAI_API_KEY``, ``OPENAI_API_KEY_2``, |
| 562 | etc. |
| 563 | |
| 564 | Parameters |
| 565 | ---------- |
| 566 | max_concurrent_per_key : |
| 567 | Maximum simultaneous requests allowed per key (default 5). |
| 568 | With 10 keys this gives 50 aggregate slots. |
| 569 | |
| 570 | Returns |
| 571 | ------- |
| 572 | ApiKeyPool or None |
| 573 | ``None`` when no multi-key configuration is detected. |
| 574 | """ |
| 575 | keys: list[ApiKey] = [] |
| 576 | |
| 577 | raw = os.environ.get(_API_KEYS_ENV, "").strip() |
| 578 | if raw: |
| 579 | for line in raw.replace(";", "\n").splitlines(): |
| 580 | line = line.strip() |
| 581 | if not line or line.startswith("#"): |
| 582 | continue |
| 583 | parts = line.split("|") |
| 584 | if len(parts) < 1: |
| 585 | continue |
| 586 | key_str = parts[0].strip() |
| 587 | base_url = parts[1].strip() if len(parts) > 1 else None |
| 588 | model = parts[2].strip() if len(parts) > 2 else "gpt-5.4" |
| 589 | keys.append(ApiKey( |
| 590 | key=key_str, base_url=base_url, model=model, |
| 591 | max_concurrent=max_concurrent_per_key, |
| 592 | )) |
| 593 | |
| 594 | if not keys: |
| 595 | base = os.environ.get("OPENAI_API_KEY", "").strip() |
| 596 | base_url = os.environ.get("OPENAI_BASE_URL", None) |
| 597 | if base: |
| 598 | keys.append(ApiKey( |
| 599 | key=base, base_url=base_url, model="gpt-5.4", |
| 600 | max_concurrent=max_concurrent_per_key, |
| 601 | )) |
| 602 | for idx in range(2, 10): |
| 603 | extra = os.environ.get(f"OPENAI_API_KEY_{idx}", "").strip() |
| 604 | if not extra: |
| 605 | break |
| 606 | keys.append(ApiKey( |
| 607 | key=extra, base_url=base_url, model="gpt-5.4", |
| 608 | max_concurrent=max_concurrent_per_key, |
| 609 | )) |